PCQRSCANER/venv/Lib/site-packages/pptx/enum/base.py
2019-12-22 21:51:47 +01:00

365 lines
10 KiB
Python

# encoding: utf-8
"""
Base classes and other objects used by enumerations
"""
from __future__ import absolute_import, print_function
import sys
import textwrap
def alias(*aliases):
"""
Decorating a class with @alias('FOO', 'BAR', ..) allows the class to
be referenced by each of the names provided as arguments.
"""
def decorator(cls):
# alias must be set in globals from caller's frame
caller = sys._getframe(1)
globals_dict = caller.f_globals
for alias in aliases:
globals_dict[alias] = cls
return cls
return decorator
class _DocsPageFormatter(object):
"""
Formats a RestructuredText documention page (string) for the enumeration
class parts passed to the constructor. An immutable one-shot service
object.
"""
def __init__(self, clsname, clsdict):
self._clsname = clsname
self._clsdict = clsdict
@property
def page_str(self):
"""
The RestructuredText documentation page for the enumeration. This is
the only API member for the class.
"""
tmpl = ".. _%s:\n\n%s\n\n%s\n\n----\n\n%s"
components = (
self._ms_name,
self._page_title,
self._intro_text,
self._member_defs,
)
return tmpl % components
@property
def _intro_text(self):
"""
The docstring of the enumeration, formatted for use at the top of the
documentation page
"""
try:
cls_docstring = self._clsdict["__doc__"]
except KeyError:
cls_docstring = ""
if cls_docstring is None:
return ""
return textwrap.dedent(cls_docstring).strip()
def _member_def(self, member):
"""
Return an individual member definition formatted as an RST glossary
entry, wrapped to fit within 78 columns.
"""
member_docstring = textwrap.dedent(member.docstring).strip()
member_docstring = textwrap.fill(
member_docstring,
width=78,
initial_indent=" " * 4,
subsequent_indent=" " * 4,
)
return "%s\n%s\n" % (member.name, member_docstring)
@property
def _member_defs(self):
"""
A single string containing the aggregated member definitions section
of the documentation page
"""
members = self._clsdict["__members__"]
member_defs = [
self._member_def(member) for member in members if member.name is not None
]
return "\n".join(member_defs)
@property
def _ms_name(self):
"""
The Microsoft API name for this enumeration
"""
return self._clsdict["__ms_name__"]
@property
def _page_title(self):
"""
The title for the documentation page, formatted as code (surrounded
in double-backtics) and underlined with '=' characters
"""
title_underscore = "=" * (len(self._clsname) + 4)
return "``%s``\n%s" % (self._clsname, title_underscore)
class MetaEnumeration(type):
"""
The metaclass for Enumeration and its subclasses. Adds a name for each
named member and compiles state needed by the enumeration class to
respond to other attribute gets
"""
def __new__(meta, clsname, bases, clsdict):
meta._add_enum_members(clsdict)
meta._collect_valid_settings(clsdict)
meta._generate_docs_page(clsname, clsdict)
return type.__new__(meta, clsname, bases, clsdict)
@classmethod
def _add_enum_members(meta, clsdict):
"""
Dispatch ``.add_to_enum()`` call to each member so it can do its
thing to properly add itself to the enumeration class. This
delegation allows member sub-classes to add specialized behaviors.
"""
enum_members = clsdict["__members__"]
for member in enum_members:
member.add_to_enum(clsdict)
@classmethod
def _collect_valid_settings(meta, clsdict):
"""
Return a sequence containing the enumeration values that are valid
assignment values. Return-only values are excluded.
"""
enum_members = clsdict["__members__"]
valid_settings = []
for member in enum_members:
valid_settings.extend(member.valid_settings)
clsdict["_valid_settings"] = valid_settings
@classmethod
def _generate_docs_page(meta, clsname, clsdict):
"""
Return the RST documentation page for the enumeration.
"""
clsdict["__docs_rst__"] = _DocsPageFormatter(clsname, clsdict).page_str
class EnumerationBase(object):
"""
Base class for all enumerations, used directly for enumerations requiring
only basic behavior. It's __dict__ is used below in the Python 2+3
compatible metaclass definition.
"""
__members__ = ()
__ms_name__ = ""
@classmethod
def validate(cls, value):
"""
Raise |ValueError| if *value* is not an assignable value.
"""
if value not in cls._valid_settings:
raise ValueError(
"%s not a member of %s enumeration" % (value, cls.__name__)
)
Enumeration = MetaEnumeration("Enumeration", (object,), dict(EnumerationBase.__dict__))
class XmlEnumeration(Enumeration):
"""
Provides ``to_xml()`` and ``from_xml()`` methods in addition to base
enumeration features
"""
__members__ = ()
__ms_name__ = ""
@classmethod
def from_xml(cls, xml_val):
"""
Return the enumeration member corresponding to the XML value
*xml_val*.
"""
return cls._xml_to_member[xml_val]
@classmethod
def to_xml(cls, enum_val):
"""
Return the XML value of the enumeration value *enum_val*.
"""
cls.validate(enum_val)
return cls._member_to_xml[enum_val]
class EnumMember(object):
"""
Used in the enumeration class definition to define a member value and its
mappings
"""
def __init__(self, name, value, docstring):
self._name = name
if isinstance(value, int):
value = EnumValue(name, value, docstring)
self._value = value
self._docstring = docstring
def add_to_enum(self, clsdict):
"""
Add a name to *clsdict* for this member.
"""
self.register_name(clsdict)
@property
def docstring(self):
"""
The description of this member
"""
return self._docstring
@property
def name(self):
"""
The distinguishing name of this member within the enumeration class,
e.g. 'MIDDLE' for MSO_VERTICAL_ANCHOR.MIDDLE, if this is a named
member. Otherwise the primitive value such as |None|, |True| or
|False|.
"""
return self._name
def register_name(self, clsdict):
"""
Add a member name to the class dict *clsdict* containing the value of
this member object. Where the name of this object is None, do
nothing; this allows out-of-band values to be defined without adding
a name to the class dict.
"""
if self.name is None:
return
clsdict[self.name] = self.value
@property
def valid_settings(self):
"""
A sequence containing the values valid for assignment for this
member. May be zero, one, or more in number.
"""
return (self._value,)
@property
def value(self):
"""
The enumeration value for this member, often an instance of
EnumValue, but may be a primitive value such as |None|.
"""
return self._value
class EnumValue(int):
"""
A named enumeration value, providing __str__ and __doc__ string values
for its symbolic name and description, respectively. Subclasses int, so
behaves as a regular int unless the strings are asked for.
"""
def __new__(cls, member_name, int_value, docstring):
return super(EnumValue, cls).__new__(cls, int_value)
def __init__(self, member_name, int_value, docstring):
super(EnumValue, self).__init__()
self._member_name = member_name
self._docstring = docstring
@property
def __doc__(self):
"""
The description of this enumeration member
"""
return self._docstring.strip()
def __str__(self):
"""
The symbolic name and string value of this member, e.g. 'MIDDLE (3)'
"""
return "{0:s} ({1:d})".format(self._member_name, self)
class ReturnValueOnlyEnumMember(EnumMember):
"""
Used to define a member of an enumeration that is only valid as a query
result and is not valid as a setting, e.g. MSO_VERTICAL_ANCHOR.MIXED (-2)
"""
@property
def valid_settings(self):
"""
No settings are valid for a return-only value.
"""
return ()
class XmlMappedEnumMember(EnumMember):
"""
Used to define a member whose value maps to an XML attribute value.
"""
def __init__(self, name, value, xml_value, docstring):
super(XmlMappedEnumMember, self).__init__(name, value, docstring)
self._xml_value = xml_value
def add_to_enum(self, clsdict):
"""
Compile XML mappings in addition to base add behavior.
"""
super(XmlMappedEnumMember, self).add_to_enum(clsdict)
self.register_xml_mapping(clsdict)
def register_xml_mapping(self, clsdict):
"""
Add XML mappings to the enumeration class state for this member.
"""
member_to_xml = self._get_or_add_member_to_xml(clsdict)
member_to_xml[self.value] = self.xml_value
xml_to_member = self._get_or_add_xml_to_member(clsdict)
xml_to_member[self.xml_value] = self.value
@property
def xml_value(self):
"""
The XML attribute value that corresponds to this enumeration value
"""
return self._xml_value
@staticmethod
def _get_or_add_member_to_xml(clsdict):
"""
Add the enum -> xml value mapping to the enumeration class state
"""
if "_member_to_xml" not in clsdict:
clsdict["_member_to_xml"] = dict()
return clsdict["_member_to_xml"]
@staticmethod
def _get_or_add_xml_to_member(clsdict):
"""
Add the xml -> enum value mapping to the enumeration class state
"""
if "_xml_to_member" not in clsdict:
clsdict["_xml_to_member"] = dict()
return clsdict["_xml_to_member"]