Overview

Name Objects

This package provides an interface for dealing with naming conventions; from defining them, to identifying names and creating new ones.

Each Name object has a config attribute that contains the fields and regex patterns of the convention to follow. Names can also drop fields from their parent classes with the drop attribute, or they can merge / split fields with the join attribute.

Composition Example

strict digraph example { // nodes creation, shape and style defined here bgcolor=invis { node [style="rounded, filled" shape=box] class, File, Pipe, BasicPipeFile } { node [style=filled margin=0 width=1 height=0.46 shape=polygon fixedsize=true skew=0.4] format, file_format, pipe_format, pipefile_format } { node [shape=none] config_patterns, file_config_patterns, pipe_config_patterns, pipefile_config_patterns } { node [style="dashed, filled" shape=box] example, file_example, pipe_example, pipefile_example } // connections, labels and color related updates by higher level groups subgraph legend { edge[style=invis] // connect with invisible edges to emulate a legend class, format, example, config_patterns [color=gray40 fillcolor=gray95] config_patterns [label="field=pattern" fontcolor=gray22] class -> config_patterns -> format -> example } File, file_format, file_example [color=lightgoldenrod3 fillcolor=lemonchiffon1] file_format [label=".{suffix}"] file_example [label=".ext"] // escape the inverse slash so generated image displays one file_config_patterns [label="suffix = \\w+" fontcolor=lightgoldenrod4] Pipe, pipe_format, pipe_example [color=lightskyblue4 fillcolor=lightblue] pipe_format [label=".{pipe}"] pipe_example [label=".1 .1.out .1.out.101"] pipe_config_patterns [label="version = \\d+ output = \\w+? index = \\d+?"] BasicPipeFile, pipefile_format, pipefile_example [color=mediumorchid4 fillcolor=plum2] pipefile_format [skew=0.15 width=2 label="{base}.{pipe}.{suffix}"] pipefile_example [label="wip_data.7.ext pipe_data.7.out.ext indexed_data.7.out.101.ext"] pipefile_config_patterns [label="base = \\w+" fontcolor=mediumorchid4] edge [color=gray36 arrowhead=vee] Pipe -> pipe_config_patterns -> pipe_format -> pipe_example File -> file_config_patterns -> file_format -> file_example BasicPipeFile -> pipefile_config_patterns -> pipefile_format -> pipefile_example {Pipe, File} -> BasicPipeFile }

Usage

Built-ins & config attribute

Inherit from the class to use and assign a class attribute config as a mapping of {field_name: regex_pattern} to use.

Name:

>>> from naming import Name
>>> class BasicName(Name):
...     config = dict(base=r'\w+')
...
>>> n = BasicName()
>>> n.get()  # no name has been set on the object, convention is solved with {missing} fields
'{base}'
>>> n.values
{}
>>> n.name = 'hello_world'
>>> n
Name("hello_world")
>>> str(n)  # cast to string
'hello_world'
>>> n.values
{'base': 'hello_world'}
>>> # modify name and get values from field names
>>> n.base = 'through_field_name'
>>> n.values
{'base': 'through_field_name'}
>>> n.base
'through_field_name'

Pipe:

>>> from naming import Pipe
>>> class BasicPipe(Pipe):
...     config = dict(base=r'\w+')
...
>>> p = BasicPipe()
>>> p.get()
'{base}.{pipe}'
>>> p.get(version=10)
'{base}.10'
>>> p.get(output='data')
'{base}.data.{version}'
>>> p.get(output='cache', version=7, index=24)
'{base}.cache.7.24'
>>> p = BasicPipe('my_wip_data.1')
>>> p.version
'1'
>>> p.values
{'base': 'my_wip_data', 'pipe': '.1', 'version': '1'}
>>> p.get(output='exchange')  # returns a new string
'my_wip_data.exchange.1'
>>> p.name
'my_wip_data.1'
>>> p.output = 'exchange'  # mutates the object
>>> p.name
'my_wip_data.exchange.1'
>>> p.index = 101
>>> p.version = 7
>>> p.name
'my_wip_data.exchange.7.101'
>>> p.values
{'base': 'my_wip_data', 'pipe': '.exchange.7.101', 'output': 'exchange', 'version': '7', 'index': '101'}

File:

>>> from naming import File
>>> class BasicFile(File):
...     config = dict(base=r'\w+')
...
>>> f = BasicFile()
>>> f.get()
'{basse}.{suffix}'
>>> f.get(suffix='png')
'{base}.png'
>>> f = BasicFile('hello.world')
>>> f.values
{'base': 'hello', 'suffix': 'world'}
>>> f.suffix
'world'
>>> f.suffix = 'abc'
>>> f.name
'hello.abc'
>>> f.path
WindowsPath('hello.abc')

PipeFile:

>>> from naming import PipeFile
>>> class BasicPipeFile(PipeFile):
...     config = dict(base=r'\w+')
...
>>> p = BasicPipeFile('wipfile.7.ext')
>>> p.values
{'base': 'wipfile', 'pipe': '.7', 'version': '7', 'suffix': 'ext'}
>>> [p.get(index=x, output='render') for x in range(10)]
['wipfile.render.7.0.ext',
'wipfile.render.7.1.ext',
'wipfile.render.7.2.ext',
'wipfile.render.7.3.ext',
'wipfile.render.7.4.ext',
'wipfile.render.7.5.ext',
'wipfile.render.7.6.ext',
'wipfile.render.7.7.ext',
'wipfile.render.7.8.ext',
'wipfile.render.7.9.ext']

Extending Names

The config, drop and join attributes are merged on subclasses.

Inheriting from an existing name:

>>> class ProjectFile(BasicPipeFile):
...     config = dict(year='[0-9]{4}',
...                   user='[a-z]+',
...                   another='(constant)',
...                   last='[a-zA-Z0-9]+')
...
>>> pf = ProjectFile('project_data_name_2017_christianl_constant_iamlast.data.17.abc', sep='_')
>>> pf.values
{'base': 'project_data_name',
'year': '2017',
'user': 'christianl',
'another': 'constant',
'last': 'iamlast',
'pipe': '.data.17',
'output': 'data',
'version': '17',
'suffix': 'abc'}
>>> pf.nice_name  # no pipe & suffix fields
'project_data_name_2017_christianl_constant_iamlast'
>>> pf.year
'2017'
>>> pf.year = 'nondigits'  # mutating with invalid fields raises a ValueError
Traceback (most recent call last):
...
ValueError: Can't set field 'year' with invalid value 'nondigits' on 'ProjectFile("project_data_name_2017_christianl_constant_iamlast.data.17.abc")'. A valid field value should match pattern: '[0-9]{4}'
>>> pf.year = 1907
>>> pf
ProjectFile("project_data_name_1907_christianl_constant_iamlast.data.17.abc")
>>> pf.suffix
'abc'
>>> pf.sep = '  '  # you can set the separator to a different set of characters
>>> pf.name
'project_data_name   1907   christianl   constant   iamlast.data.17.abc'

Dropping fields from bases:

>>> class Dropper(BasicPipeFile):
...     config = dict(without=r'[a-zA-Z0-9]+', basename=r'[a-zA-Z0-9]+')
...     drop=('base',)
...
>>> d = Dropper()
>>> d.get()
'{without}_{basename}.{pipe}.{suffix}'
>>> # New subclasses will drop the 'base' field as well
>>> Subdropper = type('Dropper', (Dropper,), dict(config=dict(subdrop='[\w]')))
>>> s = Subdropper()
>>> s.get()
'{without}_{basename}_{subdrop}.{pipe}.{suffix}'

Setting compound fields:

>>> # splitting the 'base' field into multiple joined fields
>>> class Compound(BasicPipeFile):
...     config=dict(first=r'\d+', second=r'[a-zA-Z]+')
...     join=dict(base=('first', 'second'))
...
>>> c = Compound()
>>> c.get()  # we see the original field 'base'
'{base}.{pipe}.{suffix}'
>>> c.get(first=50, second='abc')  # providing each field to join will work
'50abc.{pipe}.{suffix}'
>>> c.name = c.get(base='101dalmatians', version=1, suffix='png')  # providing the key field will also work
>>> c.nice_name
'101dalmatians'
>>> c.get(first=200)
'200dalmatians.1.png'
>>> class CompoundByDash(Compound):
...     join_sep = '-'  # you can specify the string to join compounds
...
>>> c = CompoundByDash('101-dalmatians.1.png')
>>> c.get(first=300)
'300-dalmatians.1.png'

Defining path rules for File subclasses:

>>> from naming import File
>>> class FilePath(File):
...     config = dict(base=r'\w+', extrafield='[a-z0-9]+')
...     def get_path_pattern_list(self):
...         # As an example we are returning the pattern list from the name object (base, extrafield)
...         return super().get_pattern_list()
...
>>> fp = FilePath()
>>> fp.get()
'{base} {extrafield}.{suffix}'
>>> # path attribute will vary depending on the OS
>>> fp.path
WindowsPath('{base}/{extrafield}/{base} {extrafield}.{suffix}')

Using properties as fields while solving names:

>>> from naming import PipeFile
>>> class PropertyField(PipeFile):
...     config = dict(base=r'\w+', extrafield='[a-z0-9]+')
...
...     @property
...     def nameproperty(self):
...         return 'staticvalue'
...
...     @property
...     def pathproperty(self):
...         return 'path_field'
...
...     def get_path_pattern_list(self):
...         result = super().get_pattern_list()
...         result.append('pathproperty')
...         return result
...
...     def get_pattern_list(self):
...         result = super().get_pattern_list()
...         result.append('nameproperty')
...         return result
...
>>> pf = PropertyField()
>>> pf.get()
'{base} {extrafield} staticvalue.{pipe}.{suffix}'
>>> pf.name = 'simple props staticvalue.1.abc'
>>> pf.values
{'base': 'simple',
'extrafield': 'props',
'nameproperty': 'staticvalue',
'pipe': '.1',
'version': '1',
'suffix': 'abc'}
>>> pf.path
WindowsPath('simple/props/path_field/simple props staticvalue.1.abc')