class Reinforcements(Domain): """A feature domain for specifying reinforcement signals.""" # TODO: Can probably do away with the mapping... But will need to make # changes to SimpleQNet. - Can _config = ("mapping",) def __init__( self, mapping: Dict[feature, Tuple[Hashable, int]] ) -> None: """ Initialize Reinforcements instance. :param mapping: A one-to-one mapping from features to dimensions. Each feature is assumed to represent a reinforcement signal for the corresponding dimension. Will raise a ValueError if the mapping is found not to be one-to-one. """ with self.config(): self.mapping = MappingProxyType(mapping) def update(self) -> None: if len(set(self.mapping.keys())) != len(set(self.mapping.values())): raise ValueError("Mapping must be one-to-one.") super().__init__(features=tuple(self.mapping))
def register(impl: Callable = None, *, arguments: MappingProxyType, on: Hashable): """ Registers a new implementation for the given value of key. :param on: dispatch value to register this implementation on. :param arguments: parameters to impl. :param impl: implementation to associate with value. """ if impl is None: return functools.partial(register, arguments=arguments, on=on) if on in registry: raise ValueError(f'duplicate implementation for {on!r} for {name}') # Determine index of dispatch parameter in this signature. idx = None for i, parameter in enumerate(arguments.values()): if parameter.name == key: if parameter.kind == inspect.Parameter.KEYWORD_ONLY: # Parameter is keyword-only, so it has no 'index'. idx = -1 else: idx = i registry[on] = impl, idx return impl
def __init_record(cls): """Init a list for query Hashed Map can not afford this , so we need more space to do this :return: """ od = MappingProxyType(cls._member_map_) cls.__record = list(od.values())
class CommandDispatcher: def __init__(self, bot, command, *, loop=None): self._bot = bot self._command = command self._child_dispatchers = {} self._handlers = [] self.loop = loop or asyncio.get_event_loop() self.child_dispatchers = MappingProxyType(self._child_dispatchers) @property def command(self): return self._command @property def handlers(self): return list(self._handlers) @property def user_level(self): levels = [] if self._handlers: levels += [h.user_level for h in self._handlers] if self.child_dispatchers: levels += [c.user_level for c in self.child_dispatchers.values()] levels = filter(None.__ne__, levels) return min(levels) if levels else None @property def is_leaf(self): return not self.child_dispatchers def ensure_child_dispatchers(self, commands): command, sub_commands = (commands.split(' ', 1) + [''])[:2] dispatcher = self._ensure_child_dispatcher(command) if sub_commands: return dispatcher.ensure_child_dispatchers(sub_commands) return dispatcher def _ensure_child_dispatcher(self, command): if command not in self._child_dispatchers: dispatcher = self.__class__(self._bot, command) self._child_dispatchers[command] = dispatcher return self._child_dispatchers[command] def register_handler(self, handler, command=None): if not command: self._handlers.append(handler) return self return self.ensure_child_dispatchers(command).register_handler(handler) def get(self, command_text, user_level): command, sub_commands = (command_text.split(' ', 1) + [''])[:2] command = self._get_command_from_alias(command) try: dispatcher = self.child_dispatchers[command] if dispatcher.user_level <= user_level: return dispatcher.get(sub_commands, user_level) except KeyError: pass return (self, command_text) def _get_command_from_alias(self, command): alias = self._bot.database.get_CommandAlias_by_alias(command) return alias.command if alias else command def dispatch(self, command, message): user_level = UserLevel.get(message.author, message.channel) dispatcher, attributes = self.get(command, user_level) handlers = [ h for h in dispatcher.handlers if h.user_level <= user_level ] for handler in handlers: self.loop.create_task( self._wrapper(handler, attributes, message, command)) return bool(handlers) async def _wrapper(self, handler, attributes, message, command): try: binding = self._get_binding_for(handler, attributes, message) await handler.coroutine(*binding.args, **binding.kwargs) except CommandException as ex: await self._bot.send_message(message.channel, str(ex)) except BaseException: await self._bot.send_message( message.channel, 'Oh no, something went horribly wrong trying to complete this' ' command. Please tell the owner of this bot what command you' ' entered and roughly when this happened, and I\'ll get all' ' fixed up as soon as possible. Thanks!') logging.exception('Error in command {} {}'.format( command, attributes)) def _get_binding_for(self, handler, attributes, message): signature = inspect.signature(handler.coroutine) try: if len(signature.parameters) > 1: attributes = attributes.split(' ', len(signature.parameters) - 2) return signature.bind( message, *[att for att in attributes if att is not '']) return signature.bind(message) except TypeError: raise CommandException('Syntax: `{}`'.format(handler.syntax))
class LVPPool: """ Communicates with more than one arduino simultaneously. """ @classmethod def all_devices(cls, exclude=(), kind=LVP, merge_log=True, **kwargs): """ Create an LVP pool using all connections """ kwargs.setdefault('log_id', merge_log) kwargs.setdefault('log_path_with_id', not merge_log) devices = [p.device for p in comports() if p.device not in exclude] return cls([kind(device, **kwargs) for device in devices]) def __init__(self, devices): self.devices = MappingProxyType({dev.id: dev for dev in devices}) def __iter__(self): return iter(self.devices.values()) def __len__(self): return len(self.devices) def __repr__(self): return f"<LVPPool with {[d.id for d in self]}>" def _parallel_map(self, func, devices=None, args=(), kwargs=MappingProxyType({}), timeout=None): devices = self if devices is None else list(devices) results = [None] * len(devices) def target(i, dev): results[i] = func(dev, *args, **kwargs) threads = [ Thread(target=target, args=item) for item in enumerate(devices) ] for thread in threads: thread.start() for thread in threads: thread.join(timeout=timeout) return {dev.id: res for dev, res in zip(devices, results)} def query(self, query) -> List[LVP]: """ Filter devices using query. Return a list of all selected devices. """ if query == ...: return list(self.devices.values()) elif isinstance(query, LVP): return self.query(query.id) elif isinstance(query, (list, tuple)): result = set() for q in query: result.update(self.query(q)) return list(result) elif query in self.devices: return [self.devices[query]] raise ValueError("invalid query") def get(self, ref, *args, timeout=None): """ Get set of values from all selected devices. """ devs = self.query(ref) return self._parallel_map(lambda d: d.get(*args), devs) def set(self, ref, *args, timeout=None, **kwargs): """ Set of values on all selected all devices. """ devs = self.query(ref) self._parallel_map(lambda d: d.set(*args, **kwargs), devs) def declare(self, spec, bind=True): """ Declare function. """ funcs = {dev.id: dev.declare(spec, bind=False) for dev in self} name = spec.split("(")[0] def func(query, *args, **kwargs): def target(dev): return funcs[dev.id](*args, **kwargs) return self._parallel_map(target, self.query(query)) func.__name__ = name func.__doc__ = f"Calls the {spec} lvp function" if bind: setattr(self, name, func) return func def exec(self, query, cmd): self._parallel_map(lambda d: d.exec(cmd), self.query(query)) def background(self, query, cmd): self._parallel_map(lambda d: d.exec(cmd), self.query(query))
"""MappingProxyType""" from types import MappingProxyType d = {'a': 1, 'b': 2} mp = MappingProxyType(d) # Read-only print(list(mp.keys())) print(list(mp.values())) print(mp.get('a')) print(mp.get('c', 'not found')) # Immutable # del mp['a'] # TypeError # mp['a'] = 100 # TypeError # Mutate the original dictionary d['a'] = 100 d['c'] = 'new item' del d['b'] print(mp) # Reflect the changes in original dictionary
def _get_jitclass_for_dtype(dtype, upper): """ Get the correct jitclass of NbTriMatrixBase for the data type and triangle. """ if upper: return _UPPER_JITCLASS_BY_TYPE[dtype] else: return _LOWER_JITCLASS_BY_TYPE[dtype] @nb.guvectorize( [ (nbtype[:], nbtype[:], nb.intp[:], nb.intp[:], nbtype[:]) for nbtype in NUMBA_TYPES.values() ], '(n),(),(),()->()', nopython=True, ) def getitem_advanced_int_lower(array, diag, row, col, out): """getitem_advanced_int_lower(array, diag, row, col) Numpy ufunc. Get items from the condensed lower triangle of a symmetric matrix using the equivalent of numpy advanced indexing with integer arrays on both the rows and columns of the full matrix. This is meant to be vectorized on the ``row`` and ``col`` arguments only. Row/column indices do not need to fall on the lower triangle (may be on diagonal or upper), but are not bounds checked.
def __init__(self, raster, list_of_prod_fp, channel_ids, is_flat, dst_nodata, interpolation, max_queue_size, parent_uid, key_in_parent): # Mutable attributes ******************************************************************** ** # Attributes that relates a query to a single optional computation phase self.cache_computation = None # type: Union[None, CacheComputationInfos] # Immutable attributes ****************************************************************** ** self.parent_uid = parent_uid self.key_in_parent = key_in_parent # The parameters given by user in invocation self.channel_ids = channel_ids # type: Sequence[int] self.is_flat = is_flat # type: bool self.unique_channel_ids = [] for bi in channel_ids: if bi not in self.unique_channel_ids: self.unique_channel_ids.append(bi) self.unique_channel_ids = tuple(self.unique_channel_ids) self.dst_nodata = dst_nodata # type: Union[int, float] self.interpolation = interpolation # type: str # Output max queue size (Parameter given to queue.Queue) self.max_queue_size = max_queue_size # type: int # How many arrays are requested self.produce_count = len(list_of_prod_fp) # type: int # Build CacheProduceInfos objects ************************************** to_zip = [] # The list of Footprints requested list_of_prod_fp = list_of_prod_fp # type: List[ProductionFootprint] to_zip.append(list_of_prod_fp) # Boolean attribute of each `prod_fp` # If `True` the resampling phase has to be performed on a Pool list_of_prod_same_grid = [ fp.same_grid(raster.fp) for fp in list_of_prod_fp ] # type: List[bool] to_zip.append(list_of_prod_same_grid) # Boolean attribute of each `prod_fp` # If `False` the queried footprint is outside of raster's footprint. It means that no # sampling is necessary and the outputed array will be full of `dst_nodata` list_of_prod_share_area = [ fp.share_area(raster.fp) for fp in list_of_prod_fp ] # type: List[bool] to_zip.append(list_of_prod_share_area) # The full Footprint that needs to be sampled for each `prod_fp` # Is `None` if `prod_fp` is fully outside of raster list_of_prod_sample_fp = [] # type: List[Union[None, SampleFootprint]] to_zip.append(list_of_prod_sample_fp) # The set of cache Footprints that are needed for each `prod_fp` list_of_prod_cache_fps = [] # type: List[FrozenSet[CacheFootprint]] to_zip.append(list_of_prod_cache_fps) # The list of resamplings to perform for each `prod_fp` # Always at least 1 resampling per `prod_fp` list_of_prod_resample_fps = [ ] # type: List[Tuple[ResampleFootprint, ...]] to_zip.append(list_of_prod_resample_fps) # The set of `cache_fp` necessary per `resample_fp` for each `prod_fp` list_of_prod_resample_cache_deps_fps = [ ] # type: List[Mapping[ResampleFootprint, FrozenSet[CacheFootprint]]] to_zip.append(list_of_prod_resample_cache_deps_fps) # The full Footprint that needs to be sampled par `resample_fp` for each `prod_fp` # Might be `None` if `prod_fp` is fully outside of raster list_of_prod_resample_sample_dep_fp = [ ] # type: List[Mapping[ResampleFootprint, Union[None, SampleFootprint]]] to_zip.append(list_of_prod_resample_sample_dep_fp) it = zip(list_of_prod_fp, list_of_prod_same_grid, list_of_prod_share_area) # TODO: Speed up that piece of code # - Code footprint with lower level code # - Spawn a ProcessPoolExecutor when >100 prod. (The same could be done for fp.tile). # - What about a global process pool executor in `buzz.env`? for prod_fp, same_grid, share_area in it: if not share_area: # Resampling will be performed in one pass, on the scheduler list_of_prod_sample_fp.append(None) list_of_prod_cache_fps.append(frozenset()) resample_fp = cast(ResampleFootprint, prod_fp) list_of_prod_resample_fps.append((resample_fp, )) list_of_prod_resample_cache_deps_fps.append( MappingProxyType({resample_fp: frozenset()})) list_of_prod_resample_sample_dep_fp.append( MappingProxyType({resample_fp: None})) else: if same_grid: # Remapping will be performed in one pass, on the scheduler sample_fp = raster.fp & prod_fp resample_fps = [cast(ResampleFootprint, prod_fp)] sample_dep_fp = {resample_fps[0]: sample_fp} else: sample_fp = raster.build_sampling_footprint_to_remap_interpolate( prod_fp, interpolation) if raster.max_resampling_size is None: # Remapping will be performed in one pass, on a Pool resample_fps = [cast(ResampleFootprint, prod_fp)] sample_dep_fp = {resample_fps[0]: sample_fp} else: # Resampling will be performed in several passes, on a Pool rsize = np.maximum(prod_fp.rsize, sample_fp.rsize) countx, county = np.ceil( rsize / raster.max_resampling_size).astype(int) resample_fps = prod_fp.tile_count( countx, county, boundary_effect='shrink').flatten().tolist() sample_dep_fp = { resample_fp: (raster. build_sampling_footprint_to_remap_interpolate( resample_fp, interpolation) if resample_fp.share_area(raster.fp) else None) for resample_fp in resample_fps } resample_cache_deps_fps = MappingProxyType({ resample_fp: frozenset(raster.cache_fps_of_fp(sample_subfp)) for resample_fp in resample_fps for sample_subfp in [sample_dep_fp[resample_fp]] if sample_subfp is not None }) for s in resample_cache_deps_fps.items(): assert len(s) > 0 # The `intersection of the cache_fps with sample_fp` might not be the same as the # the `intersection of the cache_fps with resample_fps`! cache_fps = frozenset( itertools.chain.from_iterable( resample_cache_deps_fps.values())) assert len(cache_fps) > 0 list_of_prod_cache_fps.append(cache_fps) list_of_prod_sample_fp.append(sample_fp) list_of_prod_resample_fps.append(tuple(resample_fps)) list_of_prod_resample_cache_deps_fps.append( resample_cache_deps_fps) list_of_prod_resample_sample_dep_fp.append( MappingProxyType(sample_dep_fp)) self.prod = tuple([CacheProduceInfos(*args) for args in zip(*to_zip) ]) # type: Tuple[CacheProduceInfos, ...] # Misc ***************************************************************** # The list of all cache Footprints needed, ordered by priority self.list_of_cache_fp = [] # type: Sequence[CacheFootprint] seen = set() for fps in list_of_prod_cache_fps: for fp in fps: if fp not in seen: seen.add(fp) self.list_of_cache_fp.append(fp) self.list_of_cache_fp = tuple(self.list_of_cache_fp) del seen # The dict of cache Footprint to set of production idxs # For each `cache_fp`, the set of prod_idx that need this cache tile self.dict_of_prod_idxs_per_cache_fp = collections.defaultdict( set) # type: Mapping[CacheFootprint, AbstractSet[int]] for i, (prod_fp, cache_fps) in enumerate( zip(list_of_prod_fp, list_of_prod_cache_fps)): for cache_fp in cache_fps: self.dict_of_prod_idxs_per_cache_fp[cache_fp].add(i) for k, v in self.dict_of_prod_idxs_per_cache_fp.items(): self.dict_of_prod_idxs_per_cache_fp[k] = frozenset(v) self.dict_of_prod_idxs_per_cache_fp = MappingProxyType( self.dict_of_prod_idxs_per_cache_fp) # The dict of cache Footprint to production_idx # For each `cache_fp`, the minimum prod_idx that need this cache tile self.dict_of_min_prod_idx_per_cache_fp = { } # type: Mapping[CacheFootprint, int] for k, v in self.dict_of_prod_idxs_per_cache_fp.items(): self.dict_of_min_prod_idx_per_cache_fp[k] = min(v) self.dict_of_min_prod_idx_per_cache_fp = MappingProxyType( self.dict_of_min_prod_idx_per_cache_fp)
class PackageLookup(SymbolLookup): """ Caching structure to make lookups on type names within a :class:`Package` faster. """ def __init__(self, archive: "Archive"): self.archive = archive data_types = {} # type: Dict[str, Tuple[TypeConName, DefDataType]] values = {} # type: Dict[str, Tuple[ValName, DefValue]] templates = {} # type: Dict[str, Tuple[TypeConName, DefTemplate]] for module in self.archive.package.modules: module_ref = ModuleRef(archive.hash, module.name) for dt in module.data_types: dt_name = TypeConName(module_ref, dt.name.segments) data_types[f"{module.name}:{dt.name}"] = (dt_name, dt) for value in module.values: value_name = ValName(module_ref, value.name_with_type.name) values[f"{module.name}:{value.name_with_type.name}"] = ( value_name, value) for tmpl in module.templates: tmpl_name = TypeConName(module_ref, tmpl.tycon.segments) templates[f"{module.name}:{tmpl.tycon}"] = (tmpl_name, tmpl) self._data_types = MappingProxyType(data_types) self._values = MappingProxyType(values) self._templates = MappingProxyType(templates) def archives(self) -> "Collection[Archive]": return [self.archive] def package_ids(self) -> "AbstractSet[PackageRef]": return frozenset([self.archive.hash]) def data_type_name(self, ref: "Any") -> "TypeConName": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: dt_name = self.local_data_type_name(name) if dt_name is not None: return dt_name raise NameNotFoundError(ref) def data_type(self, ref: "Any") -> "DefDataType": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: dt = self.local_data_type(name) if dt is not None: return dt raise NameNotFoundError(ref) def local_data_type_name(self, name: str) -> "Optional[TypeConName]": r = self._data_types.get(name) return r[0] if r is not None else None def local_data_type(self, name: str) -> "Optional[DefDataType]": """ Variation of :meth:`data_type` that assumes the name is already scoped to this package. Unlike :meth:`data_type`, this method returns ``None`` in the case of no match. You should not normally use this method directly, and instead prefer to use the methods of the :class:`SymbolLookup` protocol. :param name: A name to search for. Must be of the form ``"ModuleName:EntityName"``, where both modules and entities are dot-delimited. :return: Either a matching :class:`DefDataType`, or ``None`` if no match. """ r = self._data_types.get(name) return r[1] if r is not None else None def value(self, ref: "Any") -> "DefValue": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: dt = self.local_value(name) if dt is not None: return dt raise NameNotFoundError(ref) def local_value(self, name: str) -> "Optional[DefValue]": """ Variation of :meth:`data_type` that assumes the name is already scoped to this package. Unlike :meth:`data_type`, this method returns ``None`` in the case of no match. You should not normally use this method directly, and instead prefer to use the methods of the :class:`SymbolLookup` protocol. :param name: A name to search for. Must be of the form ``"ModuleName:EntityName"``, where both modules and entities are dot-delimited. :return: Either a matching :class:`DefDataType`, or ``None`` if no match. """ r = self._values.get(name) return r[1] if r is not None else None def template_names(self, ref: "Any") -> "Collection[TypeConName]": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: if name == "*": return self.local_template_names() elif name in self._templates: n, _ = self._templates.get(name) return n return [] def template_name(self, ref: "Any") -> "TypeConName": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: tmpl = self.local_template_name(name) if tmpl is not None: return tmpl raise NameNotFoundError(ref) def template(self, ref: "Any") -> "DefTemplate": pkg, name = validate_template(ref) if pkg == self.archive.hash or pkg == STAR: tmpl = self.local_template(name) if tmpl is not None: return tmpl raise NameNotFoundError(ref) def local_template_names(self) -> "Collection[TypeConName]": return [n for n, _ in self._templates.values()] def local_template_name(self, name: str) -> "Optional[TypeConName]": r = self._templates.get(name) return r[0] if r is not None else None def local_template(self, name: str) -> "Optional[DefTemplate]": """ Variation of :meth:`data_type` that assumes the name is already scoped to this package. Unlike :meth:`data_type`, this method returns ``None`` in the case of no match. You should not normally use this method directly, and instead prefer to use the methods of the :class:`SymbolLookup` protocol. :param name: A name to search for. Must be of the form ``"ModuleName:EntityName"``, where both modules and entities are dot-delimited. :return: Either a matching :class:`DefDataType`, or ``None`` if no match. """ r = self._templates.get(name) return r[1] if r is not None else None
class Composition: """ Defines a composition of a compound. To create a composition, use the class methods: - :meth:`from_pure` - :meth:`from_formula` - :meth:`from_mass_fractions` - :meth:`from_atomic_fractions` Use the following attributes to access the composition values: - :attr:`mass_fractions`: :class:`dict` where the keys are atomic numbers and the values weight fractions. - :attr:`atomic_fractions`: :class:`dict` where the keys are atomic numbers and the values atomic fractions. - :attr:`formula`: chemical formula The composition object is immutable, i.e. it cannot be modified once created. Equality can be checked. It is hashable. It can be pickled or copied. """ _key = object() PRECISION = 0.000000001 # 1ppb def __init__(self, key, mass_fractions, atomic_fractions, formula): """ Private constructor. It should never be used. """ if key != Composition._key: raise TypeError('Composition cannot be created using constructor') if set(mass_fractions.keys()) != set(atomic_fractions.keys()): raise ValueError('Mass and atomic fractions must have the same elements') self.mass_fractions = MappingProxyType(mass_fractions) self.atomic_fractions = MappingProxyType(atomic_fractions) self._formula = formula @classmethod def from_pure(cls, z): """ Creates a pure composition. Args: z (int): atomic number """ return cls(cls._key, {z: 1.0}, {z: 1.0}, pyxray.element_symbol(z)) @classmethod def from_formula(cls, formula): """ Creates a composition from a chemical formula. Args: formula (str): chemical formula """ atomic_fractions = convert_formula_to_atomic_fractions(formula) return cls.from_atomic_fractions(atomic_fractions) @classmethod def from_mass_fractions(cls, mass_fractions, formula=None): """ Creates a composition from a mass fraction :class:`dict`. Args: mass_fractions (dict): mass fraction :class:`dict`. The keys are atomic numbers and the values weight fractions. Wildcard are accepted, e.g. ``{5: '?', 25: 0.4}`` where boron will get a mass fraction of 0.6. formula (str): optional chemical formula for the composition. If ``None``, a formula will be generated for the composition. """ mass_fractions = process_wildcard(mass_fractions) atomic_fractions = convert_mass_to_atomic_fractions(mass_fractions) if not formula: formula = generate_name(atomic_fractions) return cls(cls._key, mass_fractions, atomic_fractions, formula) @classmethod def from_atomic_fractions(cls, atomic_fractions, formula=None): """ Creates a composition from an atomic fraction :class:`dict`. Args: atomic_fractions (dict): atomic fraction :class:`dict`. The keys are atomic numbers and the values atomic fractions. Wildcard are accepted, e.g. ``{5: '?', 25: 0.4}`` where boron will get a atomic fraction of 0.6. formula (str): optional chemical formula for the composition. If ``None``, a formula will be generated for the composition. """ atomic_fractions = process_wildcard(atomic_fractions) mass_fractions = convert_atomic_to_mass_fractions(atomic_fractions) if not formula: formula = generate_name(atomic_fractions) return cls(cls._key, mass_fractions, atomic_fractions, formula) def __len__(self): return len(self.mass_fractions) def __contains__(self, z): return z in self.mass_fractions def __iter__(self): return iter(self.mass_fractions.keys()) def __repr__(self): return '<{}({})>'.format(self.__class__.__name__, self.inner_repr()) def __eq__(self, other): if not isinstance(other, self.__class__): return False if len(self) != len(other): return False for z in self.mass_fractions: if z not in other.mass_fractions: return False fraction = self.mass_fractions[z] other_fraction = other.mass_fractions[z] if not math.isclose(fraction, other_fraction, abs_tol=self.PRECISION): return False return True def __ne__(self, other): return not self == other def __hash__(self): out = [] for z in sorted(self.mass_fractions): out.append(z) out.append(int(self.mass_fractions[z] / self.PRECISION)) return hash(tuple(out)) def __getstate__(self): return {'mass_fractions': dict(self.mass_fractions), 'atomic_fractions': dict(self.atomic_fractions), 'formula': self.formula} def __setstate__(self, state): self.mass_fractions = MappingProxyType(state.get('mass_fractions', {})) self.atomic_fractions = MappingProxyType(state.get('atomic_fractions', {})) self._formula = state.get('formula', '') def is_normalized(self): return math.isclose(sum(self.mass_fractions.values()), 1.0, abs_tol=self.PRECISION) def inner_repr(self): return ', '.join('{}: {:.4f}'.format(pyxray.element_symbol(z), mass_fraction) for z, mass_fraction in self.mass_fractions.items()) @property def formula(self): return self._formula
from types import MappingProxyType from typing import TypeVar from src.util.format import color PERMIT_ERROR = MappingProxyType({ 202: "WARNING", # order canceled 504: "DEBUG", # not connected -- always thrown on start 2103: "WARNING", # datafarm connection broken 2104: "DEBUG", # Market data farm connection is OK 2106: "DEBUG", # A historical data farm is connected. 2108: "DEBUG", # data hiccup 2158: "DEBUG", # A historical data farm is connected. }) assert not set(PERMIT_ERROR.values()) - {"DEBUG", "INFO", "WARNING"} # noinspection SpellCheckingInspection def init_sec_logger() -> Logger: seclog = getLogger("FINSEC") seclog.setLevel(INFO) seclog.addHandler(StreamHandler(sys.stderr)) seclog.handlers[0].setFormatter( Formatter( color("yellow", "{asctime} SEC-{levelname} ∷ {message}"), style="{", )) return seclog
class Snapshot: """ An immutable snapshot of contract data. """ def __init__(self, creates: Mapping[ContractId, CreateEvent], offset: Optional[str]): self._offset = offset self._creates = MappingProxyType(dict(creates)) self._contracts = ContractDataView(self._creates) self._template_ids = { cid.value_type: matching_normalizations(cid.value_type) for cid in self._creates } @property def offset(self) -> Optional[str]: """ The offset point in a stream that contains all of the creates without offsetting archives in this snapshot. This value is ``None`` when connecting to a ledger that is completely empty. """ return self._offset def earliest_contract( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None) -> Optional[ContractData]: """ Return the earliest contract in the Active Contract Set (in other words, _still_ active) that was created in the transaction stream that matches the specified filter, or ``None`` if there are no matches. """ ev = self.earliest_create(template_id, match) return ev.payload if ev is not None else None def matching_contracts( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None ) -> Mapping[ContractId, ContractData]: """""" return ContractDataView(self.matching_creates(template_id, match)) def latest_contract( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None) -> Optional[ContractData]: """ Return the contract that was created last in the transaction stream that matches the specified filter, or ``None`` if there are no matches. """ ev = self.latest_create(template_id, match) return ev.payload if ev is not None else None def earliest_create( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None) -> Optional[CreateEvent]: """ Return the earliest :class:`CreateEvent` in the Active Contract Set (in other words, the corresponding contract is _still_ active) that was created in the transaction stream that matches the specified filter, or ``None`` if there are no matches. """ wanted_template_ids = self._matching_template_ids(template_id) # in Python 3.9, dict values can be `reversed`; sadly we're not there yet for cev in reversed(list(self._creates.values())): if cev.contract_id.value_type in wanted_template_ids and is_match( match, cev.payload): return cev return None def matching_creates( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None ) -> Mapping[ContractId, CreateEvent]: """ Return the :class:`CreateEvent`s (indexed by :class:`ContractId`) whose contracts match the specified criteria. """ wanted_template_ids = self._matching_template_ids(template_id) matches = {} for cid, cev in self._creates.items(): if cev.contract_id.value_type in wanted_template_ids and is_match( match, cev.payload): matches[cev.contract_id] = cev return MappingProxyType(matches) def latest_create( self, template_id: Union[str, TypeConName], match: Optional[ContractMatch] = None) -> Optional[CreateEvent]: """ Return the contract that was created last in the transaction stream that matches the specified filter, or ``None`` if there are no matches. """ wanted_template_ids = self._matching_template_ids(template_id) for cev in self._creates.values(): if cev.contract_id.value_type in wanted_template_ids and is_match( match, cev.payload): return cev return None def _matching_template_ids( self, template_id: Union[str, TypeConName]) -> Collection[TypeConName]: t = normalize(template_id) return { tid for tid, matches in self._template_ids.items() if t in matches } @property def contracts(self) -> Mapping[ContractId, ContractData]: """ A read-only map of contract IDs to contract data. """ return self._contracts @property def creates(self) -> Mapping[ContractId, CreateEvent]: """ A map of contract IDs to :class:`CreateEvent`s that represent the current set of contracts in this ACS. :class:`CreateEvent`'s expose additional information, including signatories and observers. """ return self._creates def __bool__(self) -> bool: return bool(self._creates) def __len__(self) -> int: return len(self._creates) def __repr__(self) -> str: return f"Snapshot(len={len(self)})"
class Composition: """ Defines a composition of a compound. To create a composition, use the class methods: - :meth:`from_pure` - :meth:`from_formula` - :meth:`from_mass_fractions` - :meth:`from_atomic_fractions` Use the following attributes to access the composition values: - :attr:`mass_fractions`: :class:`dict` where the keys are atomic numbers and the values weight fractions. - :attr:`atomic_fractions`: :class:`dict` where the keys are atomic numbers and the values atomic fractions. - :attr:`formula`: chemical formula The composition object is immutable, i.e. it cannot be modified once created. Equality can be checked. It is hashable. It can be pickled or copied. """ _key = object() PRECISION = 0.000000001 # 1ppb def __init__(self, key, mass_fractions, atomic_fractions, formula): """ Private constructor. It should never be used. """ if key != Composition._key: raise TypeError("Composition cannot be created using constructor") if set(mass_fractions.keys()) != set(atomic_fractions.keys()): raise ValueError( "Mass and atomic fractions must have the same elements") self.mass_fractions = MappingProxyType(mass_fractions) self.atomic_fractions = MappingProxyType(atomic_fractions) self._formula = formula @classmethod def from_pure(cls, z): """ Creates a pure composition. Args: z (int): atomic number """ return cls(cls._key, {z: 1.0}, {z: 1.0}, pyxray.element_symbol(z)) @classmethod def from_formula(cls, formula): """ Creates a composition from a chemical formula. Args: formula (str): chemical formula """ atomic_fractions = convert_formula_to_atomic_fractions(formula) return cls.from_atomic_fractions(atomic_fractions) @classmethod def from_mass_fractions(cls, mass_fractions, formula=None): """ Creates a composition from a mass fraction :class:`dict`. Args: mass_fractions (dict): mass fraction :class:`dict`. The keys are atomic numbers and the values weight fractions. Wildcard are accepted, e.g. ``{5: '?', 25: 0.4}`` where boron will get a mass fraction of 0.6. formula (str): optional chemical formula for the composition. If ``None``, a formula will be generated for the composition. """ mass_fractions = process_wildcard(mass_fractions) atomic_fractions = convert_mass_to_atomic_fractions(mass_fractions) if not formula: formula = generate_name(atomic_fractions) return cls(cls._key, mass_fractions, atomic_fractions, formula) @classmethod def from_atomic_fractions(cls, atomic_fractions, formula=None): """ Creates a composition from an atomic fraction :class:`dict`. Args: atomic_fractions (dict): atomic fraction :class:`dict`. The keys are atomic numbers and the values atomic fractions. Wildcard are accepted, e.g. ``{5: '?', 25: 0.4}`` where boron will get a atomic fraction of 0.6. formula (str): optional chemical formula for the composition. If ``None``, a formula will be generated for the composition. """ atomic_fractions = process_wildcard(atomic_fractions) mass_fractions = convert_atomic_to_mass_fractions(atomic_fractions) if not formula: formula = generate_name(atomic_fractions) return cls(cls._key, mass_fractions, atomic_fractions, formula) def __len__(self): return len(self.mass_fractions) def __contains__(self, z): return z in self.mass_fractions def __iter__(self): return iter(self.mass_fractions.keys()) def __repr__(self): return "<{}({})>".format(self.__class__.__name__, self.inner_repr()) def __eq__(self, other): if not isinstance(other, self.__class__): return False if len(self) != len(other): return False for z in self.mass_fractions: if z not in other.mass_fractions: return False fraction = self.mass_fractions[z] other_fraction = other.mass_fractions[z] if not math.isclose( fraction, other_fraction, abs_tol=self.PRECISION): return False return True def __ne__(self, other): return not self == other def __hash__(self): out = [] for z in sorted(self.mass_fractions): out.append(z) out.append(int(self.mass_fractions[z] / self.PRECISION)) return hash(tuple(out)) def __getstate__(self): return { "mass_fractions": dict(self.mass_fractions), "atomic_fractions": dict(self.atomic_fractions), "formula": self.formula, } def __setstate__(self, state): self.mass_fractions = MappingProxyType(state.get("mass_fractions", {})) self.atomic_fractions = MappingProxyType( state.get("atomic_fractions", {})) self._formula = state.get("formula", "") def is_normalized(self): return math.isclose(sum(self.mass_fractions.values()), 1.0, abs_tol=self.PRECISION) def inner_repr(self): return ", ".join( "{}: {:.4f}".format(pyxray.element_symbol(z), mass_fraction) for z, mass_fraction in self.mass_fractions.items()) @property def formula(self): return self._formula