Source code for morphocut.str
"""This module contains operations commonly used in image processing."""
import warnings
from typing import Mapping, Optional, Sequence
from morphocut import Node, Output, RawOrVariable, ReturnOutputs, Variable
from morphocut._optional import import_optional_dependency
[docs]@ReturnOutputs
@Output("string")
class Format(Node):
"""
Format strings using :py:meth:`str.format`.
For more information see the :ref:`Python Format String Syntax <python:formatstrings>`.
Args:
fmt (str or Variable[str]): A format in which we want our string to be.
*args (Any or Variable[Any]): Positional arguments for positional fields in fmt.
_args (Sequence or Variable[Sequence]): Additional positional arguments for positional fields in fmt.
_kwargs (Mapping or Variable[Mapping]): Keyword arguments for named fields in fmt.
**kwargs (Any or Variable[Any]): Additional keyword arguments for named fields in fmt.
As positional arguments, :py:meth:`str.format` receives ``args`` then ``_args``.
As keyword arguments, :py:meth:`str.format` receives ``_kwargs`` then ``kwargs``.
This means that keys passed as keyword arguments overwrite keys in a dict passed as ``_kwargs``.
Example:
.. code-block:: python
fmt = "{},{},{},{},{},{},{a},{b},{c},{d}"
args = (1, 2, 3)
_args = (4, 5, 6)
_kwargs = {"a": 7, "b": 8}
kwargs = {"c": 9, "d": 10}
with Pipeline() as pipeline:
result = Format(fmt, *args, _args=_args, _kwargs=_kwargs, **kwargs)
Result: `obj[result]` == `"1,2,3,4,5,6,7,8,9,10"` for a stream object `obj`.
"""
def __init__(
self,
fmt: RawOrVariable[str],
*args: RawOrVariable,
_args: Optional[RawOrVariable[Sequence]] = None,
_kwargs: RawOrVariable[Mapping] = None,
**kwargs: RawOrVariable
):
super().__init__()
self.fmt = fmt
self._args = _args or ()
self._kwargs = _kwargs or {}
self.args = args
self.kwargs = kwargs
def transform(
self, fmt: str, _args: tuple, _kwargs: dict, args: tuple, kwargs: dict
):
kwargs = {**_kwargs, **kwargs}
return fmt.format(*args, *_args, **kwargs)
class ParseWarning(UserWarning):
"""Issued by :py:class:`Parse`."""
[docs]@ReturnOutputs
@Output("meta")
class Parse(Node):
"""
Parse information from a string.
Parse strings using a specification based on the :ref:`Python Format String Syntax <python:formatstrings>`.
.. note::
The external dependency `parse`_ is required to use this Node.
.. _parse: https://github.com/r1chardj0n3s/parse
Args:
fmt (str): The pattern to look for in the input.
string (str): A string input which is to be parsed
case_sensitive (bool): Match pattern with case.
Example:
.. code-block:: python
fmt = "This is a {named}"
string = "This is a TEST"
case_sensitive = True
with Pipeline() as pipeline:
result = Parse(fmt, string, case_sensitive)
Result: ``obj[result] == {'named': 'TEST'}`` for a stream object `obj`.
"""
def __init__(
self,
fmt: RawOrVariable[str],
string: RawOrVariable,
case_sensitive: bool = False,
):
super().__init__()
self.fmt = fmt
self.string = string
self.case_sensitive = case_sensitive
self._parse = import_optional_dependency("parse")
@self._parse.with_pattern(".*")
def parse_greedystar(text):
return text
self._extra_types = {"greedy": parse_greedystar}
if not isinstance(fmt, Variable):
self._parser = self._compile(fmt)
else:
self._parser = None
def _compile(self, fmt):
parser = self._parse.compile(
fmt, extra_types=self._extra_types, case_sensitive=self.case_sensitive
)
if not parser._named_fields:
warnings.warn(
"Pattern does not include any named fields: {}".format(fmt),
ParseWarning,
)
return parser
def transform(self, fmt, string):
parser = self._parser
if parser is None:
parser = self._compile(fmt)
result = parser.parse(string)
if result is None:
raise ValueError(
"No match for {} in {}".format(self._parser._format, string)
)
return result.named