summaryrefslogtreecommitdiff
path: root/modules/language/python/module/types.scm
diff options
context:
space:
mode:
authorStefan Israelsson Tampe <stefan.itampe@gmail.com>2018-04-13 21:51:34 +0200
committerStefan Israelsson Tampe <stefan.itampe@gmail.com>2018-04-13 21:51:34 +0200
commit6459b750a435d65a7e4cc2ddbdec2a729548d2f4 (patch)
tree21ebc5a7335708172fcc298b173051d6c643fce7 /modules/language/python/module/types.scm
parent895a5f7a8e6ab92b56f19810e94320b8d337c4a8 (diff)
enum.py compiles and loads
Diffstat (limited to 'modules/language/python/module/types.scm')
-rw-r--r--modules/language/python/module/types.scm369
1 files changed, 369 insertions, 0 deletions
diff --git a/modules/language/python/module/types.scm b/modules/language/python/module/types.scm
new file mode 100644
index 0000000..840b901
--- /dev/null
+++ b/modules/language/python/module/types.scm
@@ -0,0 +1,369 @@
+(define-module (language python module types)
+ #:use-module (oop goops)
+ #:use-module (oop pf-objects)
+ #:use-module (language python exceptions)
+ #:use-module (language python def)
+ #:use-module (language python bool)
+ #:use-module ((language python module python)
+ #:select (getattr type))
+ #:export (MappingProxyType DynamicClassAttribute Functiontype LambdaType))
+
+"""
+Define names for built-in types that aren't directly accessible as a builtin.
+"""
+(define MappingProxyType <hashtable>)
+(define FunctionType <procedure>)
+(define LambdaType <procedure>)
+
+(define-python-class DynamicClassAttribute ()
+ "Route attribute access on a class to __getattr__.
+
+ This is a descriptor, used to define attributes that act differently when
+ accessed through an instance and through a class. Instance access remains
+ normal, but access to an attribute through a class will be routed to the
+ class's __getattr__ method; this is done by raising AttributeError.
+
+ This allows one to have properties active on an instance, and have virtual
+ attributes on the class with the same name (see Enum for an example).
+ "
+
+ (define __init__
+ (lam (self (= fget None) (= fset None) (= fdel None) (= doc None))
+ (set self 'fget fget)
+ (set self 'fset fset)
+ (set self 'fdel fdel)
+
+ ;; next two lines make DynamicClassAttribute act the same as property
+ (set self '__doc__ (or (bool doc) (ref fget '__doc__)))
+ (set self 'overwrite_doc (eq? doc None))
+
+ ;; support for abstract methods
+ (set self '__isabstractmethod__
+ (bool (getattr fget '__isabstractmethod__' #f)))))
+
+ (define __get__
+ (lam (self instance (= ownerclass None))
+ (cond
+ ((eq? instance None)
+ (if (bool (ref self '__isabstractmethod__))
+ self
+ (raise (AttributeError))))
+ ((eq? (ref self 'fget) None)
+ (raise (AttributeError "unreadable attribute")))
+ (else
+ ((ref self 'fget) instance)))))
+
+ (define __set__
+ (lambda (self instance value)
+ (if (eq? (ref self 'fset) None)
+ (raise (AttributeError "can't set attribute"))
+ ((ref self 'fset) instance value))))
+
+ (define __delete__
+ (lambda (self instance)
+ (if (eq? (ref self 'fdel) None)
+ (raise (AttributeError "can't delete attribute"))
+ ((ref self 'fdel) instance))))
+
+ (define getter
+ (lambda (self fget)
+ (let* ((fdoc (if (ref self 'overwrite_doc)
+ (ref fget '__doc__)
+ None))
+ (result ((type self)
+ fget
+ (ref self 'fset)
+ (ref self 'fdel)
+ (or (bool fdoc) (ref self '__doc__)))))
+
+ (set result 'overwrite_doc (ref self 'overwrite_doc))
+ result)))
+
+ (define setter
+ (lambda (self fset)
+ (let ((result ((type self)
+ (ref self 'fget)
+ fset
+ (ref self 'fdel)
+ (ref self '__doc__))))
+
+ (set result 'overwrite_doc (ref self 'overwrite_doc))
+ result)))
+
+ (define deleter
+ (lambda (self fdel)
+ (let ((result ((type self)
+ (ref self 'fget)
+ (ref self 'fset)
+ fdel
+ (ref self '__doc__))))
+
+ (set result 'overwrite_doc (ref self 'overwrite_doc))
+ result))))
+
+#|
+import sys
+
+# Iterators in Python aren't a matter of type but of protocol. A large
+# and changing number of builtin types implement *some* flavor of
+# iterator. Don't check the type! Use hasattr to check for both
+# "__iter__" and "__next__" attributes instead.
+
+def _f(): pass
+FunctionType = type(_f)
+LambdaType = type(lambda: None) # Same as FunctionType
+MappingProxyType = type(type.__dict__)
+CodeType = type(_f.__code__)
+
+SimpleNamespace = type(sys.implementation)
+
+def _g():
+ yield 1
+GeneratorType = type(_g())
+
+async def _c(): pass
+_c = _c()
+CoroutineType = type(_c)
+_c.close() # Prevent ResourceWarning
+
+async def _ag():
+ yield
+_ag = _ag()
+AsyncGeneratorType = type(_ag)
+
+class _C:
+ def _m(self): pass
+MethodType = type(_C()._m)
+
+BuiltinFunctionType = type(len)
+BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
+
+ModuleType = type(sys)
+
+try:
+ raise TypeError
+except TypeError:
+ tb = sys.exc_info()[2]
+ TracebackType = type(tb)
+ FrameType = type(tb.tb_frame)
+ tb = None; del tb
+
+# For Jython, the following two types are identical
+GetSetDescriptorType = type(FunctionType.__code__)
+MemberDescriptorType = type(FunctionType.__globals__)
+
+del sys, _f, _g, _C, _c, # Not for export
+
+
+# Provide a PEP 3115 compliant mechanism for class creation
+def new_class(name, bases=(), kwds=None, exec_body=None):
+ """Create a class object dynamically using the appropriate metaclass."""
+ meta, ns, kwds = prepare_class(name, bases, kwds)
+ if exec_body is not None:
+ exec_body(ns)
+ return meta(name, bases, ns, **kwds)
+
+def prepare_class(name, bases=(), kwds=None):
+ """Call the __prepare__ method of the appropriate metaclass.
+
+ Returns (metaclass, namespace, kwds) as a 3-tuple
+
+ *metaclass* is the appropriate metaclass
+ *namespace* is the prepared class namespace
+ *kwds* is an updated copy of the passed in kwds argument with any
+ 'metaclass' entry removed. If no kwds argument is passed in, this will
+ be an empty dict.
+ """
+ if kwds is None:
+ kwds = {}
+ else:
+ kwds = dict(kwds) # Don't alter the provided mapping
+ if 'metaclass' in kwds:
+ meta = kwds.pop('metaclass')
+ else:
+ if bases:
+ meta = type(bases[0])
+ else:
+ meta = type
+ if isinstance(meta, type):
+ # when meta is a type, we first determine the most-derived metaclass
+ # instead of invoking the initial candidate directly
+ meta = _calculate_meta(meta, bases)
+ if hasattr(meta, '__prepare__'):
+ ns = meta.__prepare__(name, bases, **kwds)
+ else:
+ ns = {}
+ return meta, ns, kwds
+
+def _calculate_meta(meta, bases):
+ """Calculate the most derived metaclass."""
+ winner = meta
+ for base in bases:
+ base_meta = type(base)
+ if issubclass(winner, base_meta):
+ continue
+ if issubclass(base_meta, winner):
+ winner = base_meta
+ continue
+ # else:
+ raise TypeError("metaclass conflict: "
+ "the metaclass of a derived class "
+ "must be a (non-strict) subclass "
+ "of the metaclasses of all its bases")
+ return winner
+
+class DynamicClassAttribute:
+ """Route attribute access on a class to __getattr__.
+
+ This is a descriptor, used to define attributes that act differently when
+ accessed through an instance and through a class. Instance access remains
+ normal, but access to an attribute through a class will be routed to the
+ class's __getattr__ method; this is done by raising AttributeError.
+
+ This allows one to have properties active on an instance, and have virtual
+ attributes on the class with the same name (see Enum for an example).
+
+ """
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+ self.fget = fget
+ self.fset = fset
+ self.fdel = fdel
+ # next two lines make DynamicClassAttribute act the same as property
+ self.__doc__ = doc or fget.__doc__
+ self.overwrite_doc = doc is None
+ # support for abstract methods
+ self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
+
+ def __get__(self, instance, ownerclass=None):
+ if instance is None:
+ if self.__isabstractmethod__:
+ return self
+ raise AttributeError()
+ elif self.fget is None:
+ raise AttributeError("unreadable attribute")
+ return self.fget(instance)
+
+ def __set__(self, instance, value):
+ if self.fset is None:
+ raise AttributeError("can't set attribute")
+ self.fset(instance, value)
+
+ def __delete__(self, instance):
+ if self.fdel is None:
+ raise AttributeError("can't delete attribute")
+ self.fdel(instance)
+
+ def getter(self, fget):
+ fdoc = fget.__doc__ if self.overwrite_doc else None
+ result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
+ result.overwrite_doc = self.overwrite_doc
+ return result
+
+ def setter(self, fset):
+ result = type(self)(self.fget, fset, self.fdel, self.__doc__)
+ result.overwrite_doc = self.overwrite_doc
+ return result
+
+ def deleter(self, fdel):
+ result = type(self)(self.fget, self.fset, fdel, self.__doc__)
+ result.overwrite_doc = self.overwrite_doc
+ return result
+
+
+import functools as _functools
+import collections.abc as _collections_abc
+
+class _GeneratorWrapper:
+ # TODO: Implement this in C.
+ def __init__(self, gen):
+ self.__wrapped = gen
+ self.__isgen = gen.__class__ is GeneratorType
+ self.__name__ = getattr(gen, '__name__', None)
+ self.__qualname__ = getattr(gen, '__qualname__', None)
+ def send(self, val):
+ return self.__wrapped.send(val)
+ def throw(self, tp, *rest):
+ return self.__wrapped.throw(tp, *rest)
+ def close(self):
+ return self.__wrapped.close()
+ @property
+ def gi_code(self):
+ return self.__wrapped.gi_code
+ @property
+ def gi_frame(self):
+ return self.__wrapped.gi_frame
+ @property
+ def gi_running(self):
+ return self.__wrapped.gi_running
+ @property
+ def gi_yieldfrom(self):
+ return self.__wrapped.gi_yieldfrom
+ cr_code = gi_code
+ cr_frame = gi_frame
+ cr_running = gi_running
+ cr_await = gi_yieldfrom
+ def __next__(self):
+ return next(self.__wrapped)
+ def __iter__(self):
+ if self.__isgen:
+ return self.__wrapped
+ return self
+ __await__ = __iter__
+
+def coroutine(func):
+ """Convert regular generator function to a coroutine."""
+
+ if not callable(func):
+ raise TypeError('types.coroutine() expects a callable')
+
+ if (func.__class__ is FunctionType and
+ getattr(func, '__code__', None).__class__ is CodeType):
+
+ co_flags = func.__code__.co_flags
+
+ # Check if 'func' is a coroutine function.
+ # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
+ if co_flags & 0x180:
+ return func
+
+ # Check if 'func' is a generator function.
+ # (0x20 == CO_GENERATOR)
+ if co_flags & 0x20:
+ # TODO: Implement this in C.
+ co = func.__code__
+ func.__code__ = CodeType(
+ co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
+ co.co_stacksize,
+ co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
+ co.co_code,
+ co.co_consts, co.co_names, co.co_varnames, co.co_filename,
+ co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
+ co.co_cellvars)
+ return func
+
+ # The following code is primarily to support functions that
+ # return generator-like objects (for instance generators
+ # compiled with Cython).
+
+ @_functools.wraps(func)
+ def wrapped(*args, **kwargs):
+ coro = func(*args, **kwargs)
+ if (coro.__class__ is CoroutineType or
+ coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
+ # 'coro' is a native coroutine object or an iterable coroutine
+ return coro
+ if (isinstance(coro, _collections_abc.Generator) and
+ not isinstance(coro, _collections_abc.Coroutine)):
+ # 'coro' is either a pure Python generator iterator, or it
+ # implements collections.abc.Generator (and does not implement
+ # collections.abc.Coroutine).
+ return _GeneratorWrapper(coro)
+ # 'coro' is either an instance of collections.abc.Coroutine or
+ # some other object -- pass it through.
+ return coro
+
+ return wrapped
+
+
+__all__ = [n for n in globals() if n[:1] != '_']
+|#