Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
What's new
8
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Menu
Open sidebar
SageMath
SageMath Developers
tracmirror
Compare Revisions
develop...u/SimonKing/metaclass_framework
Commits (1)
Metaclass framework: Proof of concept
· 4d493e08
Simon King
authored
Oct 12, 2016
4d493e08
Hide whitespace changes
Inline
Side-by-side
src/module_list.py
View file @
4d493e08
...
...
@@ -1802,6 +1802,9 @@ ext_modules = [
sources
=
[
'sage/structure/element.pyx'
],
extra_compile_args
=
[
"-Os"
]),
Extension
(
'sage.structure.metaclass.metaclass'
,
sources
=
[
'sage/structure/metaclass/metaclass.pyx'
]),
Extension
(
'*'
,
[
'sage/structure/*.pyx'
]),
################################
...
...
src/sage/structure/metaclass/__init__.py
0 → 100644
View file @
4d493e08
src/sage/structure/metaclass/metaclass.pyx
0 → 100644
View file @
4d493e08
"""
SageMath metaclass framework
Any metaclass used in Sage is supposed to be instance (not sub-class)
of SageMetaclass. This is achieved by putting `__metaclass__ = sage_metaclass`
into the metaclass definition.
"""
try
:
import
copyreg
except
:
import
copy_reg
as
copyreg
from
sage.misc.cachefunc
import
cached_function
class
SageMetaclass
(
type
):
def
__call__
(
mcls
,
name
,
bases
,
namespace
):
basemetacls
=
[
type
(
X
)
for
X
in
bases
]
basemetacls
=
tuple
(
sorted
(
set
([
mcls
]
+
[
X
for
X
in
basemetacls
if
not
(
issubclass
(
mcls
,
X
))]),
cmp
=
lambda
x
,
y
:
cmp
(
x
.
__name__
,
y
.
__name__
)))
if
len
(
basemetacls
)
>
1
:
mcls
=
sage_metaclass
(
None
,
basemetacls
,
{})
out
=
mcls
.
__new__
(
mcls
,
name
,
bases
,
namespace
)
if
mcls
.
__init__
is
not
type
.
__init__
:
mcls
.
__init__
(
out
,
name
,
bases
,
namespace
)
return
out
class
DynamicSageMetaclass
(
SageMetaclass
):
# This is automatically created. Pickling onls works
# under the assumption that all the methods are
# inherited from the bases.
def
__reduce__
(
mcls
):
return
sage_metaclass
,
(
mcls
.
__name__
,
mcls
.
__bases__
,
{})
copyreg
.
pickle
(
DynamicSageMetaclass
,
DynamicSageMetaclass
.
__reduce__
)
_cache
=
{}
def
sage_metaclass
(
name
,
bases
,
namespace
):
#print "new", name, bases, namespace
# There are two cases: If the namespace is empty,
# then we consider a dynamically created metaclass.
# It is uniquely determined by name and bases.
# If it isn't empty, then we assume that the metaclass
# is defined in a module.
if
not
namespace
:
if
name
is
None
:
name
=
''
.
join
([
X
.
__name__
.
split
(
'Metaclass'
)[
0
]
for
X
in
bases
])
+
'Metaclass'
try
:
return
_cache
[
name
,
bases
]
except
KeyError
:
pass
mcls
=
DynamicSageMetaclass
(
name
,
bases
,
namespace
)
for
X
in
reversed
(
mcls
.
__mro__
):
try
:
metainit
=
X
.
__dict__
[
'__metainit__'
]
except
(
AttributeError
,
KeyError
):
continue
metainit
(
mcls
,
name
,
bases
,
namespace
)
_cache
[
name
,
bases
]
=
mcls
return
mcls
else
:
if
not
bases
:
bases
=
(
type
,)
return
SageMetaclass
(
name
,
bases
,
namespace
)
###################################################################
# Examples
###################################################################
#
# Unique representation
class
ClasscallMetaclass
:
__metaclass__
=
sage_metaclass
def
__call__
(
cls
,
*
args
,
**
opts
):
try
:
classcall
=
cls
.
__classcall__
except
AttributeError
:
return
type
.
__call__
(
cls
,
*
args
,
**
opts
)
return
classcall
(
cls
,
*
args
,
**
opts
)
class
MyUniqueRepresentation
:
__metaclass__
=
ClasscallMetaclass
@
cached_function
def
__classcall__
(
cls
,
*
args
,
**
opts
):
out
=
super
(
cls
,
cls
).
__new__
(
cls
,
*
args
,
**
opts
)
cls
.
__init__
(
out
,
*
args
,
**
opts
)
out
.
_reduction
=
(
type
(
out
),
args
,
opts
)
return
out
def
__hash__
(
self
):
return
id
(
self
)
def
__reduce__
(
self
):
return
unreduce
,
self
.
_reduction
def
unreduce
(
cls
,
args
,
opts
):
#print cls, args, opts
return
cls
(
*
args
,
**
opts
)
#
# Nested pickling
from
sage.misc.nested_class
import
nested_pickle
class
NestedClassMetaclass
:
__metaclass__
=
sage_metaclass
def
__init__
(
cls
,
name
,
bases
,
namespace
):
nested_pickle
(
cls
)
class
MyNestedClass
:
__metaclass__
=
NestedClassMetaclass
def
__init__
(
self
,
a
,
b
):
self
.
a
=
a
self
.
b
=
b
class
Test
(
MyUniqueRepresentation
):
def
bla
(
self
):
return
5
#
# Dynamic class
class
DynamicClassMetaclass
:
__metaclass__
=
sage_metaclass
def
__init__
(
cls
,
name
,
bases
,
namespace
):
if
'_reduction_data'
not
in
namespace
:
cls
.
_reduction_data
=
(
generic_unpickle
,
(
cls
.
__module__
,
cls
.
__name__
))
@
staticmethod
def
__metainit__
(
mcls
,
name
,
bases
,
namespace
):
copyreg
.
pickle
(
mcls
,
DynamicClassMetaclass
.
__reduce__
)
def
__reduce__
(
cls
):
red_data
=
getattr
(
cls
,
'_reduction_data'
,
None
)
if
red_data
is
None
:
clsmod
=
__import__
(
cls
.
__module__
)
return
getattr
,
(
clsmod
,
cls
.
__name__
)
return
red_data
def
generic_unpickle
(
mod
,
name
):
clsmod
=
__import__
(
mod
)
return
getattr
(
clsmod
,
name
)
copyreg
.
pickle
(
DynamicClassMetaclass
,
DynamicClassMetaclass
.
__reduce__
)
dynamic_cache
=
{}
def
my_dynamic_class
(
name
,
bases
,
extra_class
=
None
,
reduction
=
None
):
try
:
return
dynamic_cache
[
name
,
bases
,
extra_class
,
reduction
]
except
KeyError
:
pass
if
reduction
is
None
:
reduction
=
(
my_dynamic_class
,
(
name
,
bases
,
extra_class
,
reduction
))
if
extra_class
is
not
None
:
methods
=
dict
(
extra_class
.
__dict__
)
if
methods
.
has_key
(
"__dict__"
):
methods
.
__delitem__
(
"__dict__"
)
bases
=
extra_class
.
__bases__
+
bases
else
:
assert
bases
!=
()
extra_class
=
bases
[
0
]
methods
=
{}
methods
[
'_reduction_data'
]
=
reduction
methods
[
'__module__'
]
=
extra_class
.
__module__
dynamic_cache
[
name
,
bases
,
extra_class
,
reduction
]
=
DynamicClassMetaclass
(
name
,
bases
,
methods
)
return
dynamic_cache
[
name
,
bases
,
extra_class
,
reduction
]