Source code for codestare.compiletools.options
"""
Options for compilation are represented using :class:`.CompileOption`
"""
import operator
from enum import Flag, auto
from functools import reduce
from itertools import chain
from typing import List, Union
CompileOptionStr = Union['Compiler.CompileOption', str]
[docs]class CompileOption(Flag):
"""
Describes available compile options
"""
# empty docstrings for enum values to include them in documentation
#:
JAVA = auto()
#:
JSLIBRARY = auto()
#:
JSINDIVIDUAL = auto()
#:
CSHARP = auto()
#:
CPP = auto()
#:
PYTHON_BETTER_PROTO = auto()
#:
PYTHON_MYPY = auto()
#:
PYTHON_PROTOPLUS = auto()
#:
PYTHON_BASIC = auto()
#:
JAVASCRIPT = JSINDIVIDUAL | JSLIBRARY
#:
PYTHON = PYTHON_MYPY | PYTHON_BETTER_PROTO | PYTHON_PROTOPLUS | PYTHON_BASIC
#:
ALL = JAVA | JAVASCRIPT | CSHARP | CPP | PYTHON
@property
def protoc_plugin_name(self):
if self == self.JAVA: return 'java'
if self == self.JSLIBRARY: return 'js'
if self == self.JSINDIVIDUAL: return 'js'
if self == self.CSHARP: return 'csharp'
if self == self.CPP: return 'cpp'
if self == self.PYTHON_BETTER_PROTO: return 'python_betterproto'
if self == self.PYTHON_MYPY: return 'mypy'
if self == self.PYTHON_BASIC: return 'python'
if self == self.PYTHON_PROTOPLUS: return 'proto-plus'
# combined options don't have plugins
[docs] @classmethod
def from_str(cls, value) -> 'CompileOption':
"""
Convert a string to an enum value
Args:
value (str): valid values are defined by :attr:`.arguments`
Returns:
CompileOption: Enum flag
"""
matching = reduce(operator.and_, [o for o in cls if value in o.arguments])
if not matching:
raise ValueError(f"{value} is no valid option.")
return matching
[docs] @classmethod
def from_string_list(cls, values):
"""
Convert a string list to an enum value (combined flag for all values in list)
Args:
values (List[str]): list of values according to :attr:`.arguments`
Returns:
CompileOption: combined flag
"""
return reduce(operator.and_, [cls.from_str(v) for v in values])
@property
def arguments(self) -> List:
"""
Arguments that can be used to trigger this compilation
"""
arguments = []
if self & self.JAVA: arguments += ['java']
if self & self.CSHARP: arguments += ['cs']
if self & self.CPP: arguments += ['cpp']
if self & self.PYTHON_MYPY: arguments += ['mypy']
if self & self.PYTHON_BETTER_PROTO: arguments += ['better']
if self & self.PYTHON_PROTOPLUS: arguments += ['plus']
if self & self.PYTHON_BASIC: arguments += ['py']
# other options have no associated arguments
# combined options with special arguments:
if self == self.ALL:
return ['all']
if self == self.JAVASCRIPT:
return ['js']
return arguments
def format_out(self, output, *params, **kw_params):
return ':'.join(filter(None, (self.parameters(*params, **kw_params), output)))
@property
def formatted_argument(self):
return f"[{','.join(self.arguments)}] {self}" if len(self.arguments) == 1 else None
@property
def disjunct(self) -> List['CompileOption']:
"""
Return all options which are not combinations of other options
"""
return [o for o in type(self) if o & self and not o.is_composite]
@property
def is_composite(self):
"""
Like normal integer flags, individual flags are all powers of 2,
see https://docs.python.org/3/library/enum.html#flag
combined flags are not, so we test that with ``o.value & (o.value - 1)``
"""
return self.value & (self.value - 1)
[docs] def parameters(self, *args, **kwargs):
"""
some options can use request parameters for the plugin, this is e.g.
how the javascript plugin implements "library / binary" compilation.
- :attr:`.JSLIBRARY` adds ``library='protobuf_library',binary``
- :attr:`.JSINDIVIDUAL` adds ``import_style='commonjs',binary``
- :attr:`.PYTHON_PROTOPLUS` adds ``readable_imports``
"""
args = set(args)
if self.is_composite:
raise ValueError("parameters only make sense for individual options.")
if self == self.JSLIBRARY:
kwargs['library'] = 'protobuf_library'
args.add('binary')
if self == self.JSINDIVIDUAL:
kwargs['import_style'] = 'commonjs'
args.add('binary')
return ','.join(chain((f'{k}={v}' for k, v in kwargs.items()), args))
def __str__(self):
if len(self.disjunct) > 1:
return " | ".join(str(o) for o in self.disjunct)
if self == self.PYTHON_BETTER_PROTO: return 'python with `better_proto` plugin'
if self == self.PYTHON_MYPY: return 'mypy python stubs'
if self == self.PYTHON_MYPY: return 'python with `proto-plus` plugin'
if self == self.PYTHON_BASIC: return 'python'
if self == self.JSLIBRARY: return 'javascript as library'
if self == self.JSINDIVIDUAL: return 'javascript individual'
# other options can just use the name
return self.name.lower() if self.name else repr(self)