From a87c180e6dbfdc6b065d337d06211b53857d52a3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 15 Jun 2026 13:26:42 +0000 Subject: [PATCH 1/2] Add interactive overview docs REPLs Co-authored-by: chrizzftd --- docs/source/Overview.rst | 249 ++---------------- .../_static/pyrepl/overview_builtins.py | 120 +++++++++ .../_static/pyrepl/overview_extending.py | 162 ++++++++++++ docs/source/conf.py | 5 +- 4 files changed, 310 insertions(+), 226 deletions(-) create mode 100644 docs/source/_static/pyrepl/overview_builtins.py create mode 100644 docs/source/_static/pyrepl/overview_extending.py diff --git a/docs/source/Overview.rst b/docs/source/Overview.rst index b7009d1..83cbc07 100644 --- a/docs/source/Overview.rst +++ b/docs/source/Overview.rst @@ -20,237 +20,36 @@ Usage Inherit from the class to use and assign a class attribute `config` as a mapping of {field_name: regex_pattern} to use. - Name:: + The interactive REPL below defines ``BasicName``, ``BasicPipe``, + ``BasicFile`` and ``BasicPipeFile``, runs the overview examples, and keeps + the objects available at the prompt. Try changing ``n``, ``p``, ``f`` or + ``pf`` after the examples finish running. - >>> 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' + .. raw:: html - 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'] + + .. topic:: 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}') + This REPL defines and runs the subclassing, field dropping, compound field, + path, and property-backed field examples. It leaves ``project_file``, + ``dropper``, ``subdropper``, ``compound``, ``compound_by_dash``, + ``file_path`` and ``property_field`` available for further experimentation. - Using properties as fields while solving names:: + .. raw:: html - >>> 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') + + diff --git a/docs/source/_static/pyrepl/overview_builtins.py b/docs/source/_static/pyrepl/overview_builtins.py new file mode 100644 index 0000000..b7dbdaf --- /dev/null +++ b/docs/source/_static/pyrepl/overview_builtins.py @@ -0,0 +1,120 @@ +"""Interactive Overview examples for the built-in naming classes.""" + +from pprint import pprint + +from naming import File, Name, Pipe, PipeFile + + +class BasicName(Name): + config = dict(base=r'\w+') + + +class BasicPipe(Pipe): + config = dict(base=r'\w+') + + +class BasicFile(File): + config = dict(base=r'\w+') + + +class BasicPipeFile(PipeFile): + config = dict(base=r'\w+') + + +def show(expression, value): + print(f">>> {expression}") + pprint(value) + + +def run_name_example(): + global n + + print("\nName") + n = BasicName() + show("n.get()", n.get()) + show("n.values", n.values) + + print(">>> n.name = 'hello_world'") + n.name = 'hello_world' + show("n", n) + show("str(n)", str(n)) + show("n.values", n.values) + + print(">>> n.base = 'through_field_name'") + n.base = 'through_field_name' + show("n.values", n.values) + show("n.base", n.base) + + +def run_pipe_example(): + global p + + print("\nPipe") + p = BasicPipe() + show("p.get()", p.get()) + show("p.get(version=10)", p.get(version=10)) + show("p.get(output='data')", p.get(output='data')) + show("p.get(output='cache', version=7, index=24)", p.get(output='cache', version=7, index=24)) + + print(">>> p = BasicPipe('my_wip_data.1')") + p = BasicPipe('my_wip_data.1') + show("p.version", p.version) + show("p.values", p.values) + show("p.get(output='exchange')", p.get(output='exchange')) + show("p.name", p.name) + + print(">>> p.output = 'exchange'") + p.output = 'exchange' + show("p.name", p.name) + + print(">>> p.index = 101") + p.index = 101 + print(">>> p.version = 7") + p.version = 7 + show("p.name", p.name) + show("p.values", p.values) + + +def run_file_example(): + global f + + print("\nFile") + f = BasicFile() + show("f.get()", f.get()) + show("f.get(suffix='png')", f.get(suffix='png')) + + print(">>> f = BasicFile('hello.world')") + f = BasicFile('hello.world') + show("f.values", f.values) + show("f.suffix", f.suffix) + + print(">>> f.suffix = 'abc'") + f.suffix = 'abc' + show("f.name", f.name) + show("f.path", f.path) + + +def run_pipefile_example(): + global pf + + print("\nPipeFile") + print(">>> pf = BasicPipeFile('wipfile.7.ext')") + pf = BasicPipeFile('wipfile.7.ext') + show("pf.values", pf.values) + show("[pf.get(index=x, output='render') for x in range(10)]", [ + pf.get(index=x, output='render') for x in range(10) + ]) + + +def run_all_examples(): + run_name_example() + run_pipe_example() + run_file_example() + run_pipefile_example() + + +def setup(): + print("Loaded BasicName, BasicPipe, BasicFile, and BasicPipeFile.") + print("The overview examples are running now; use the prompt afterward to experiment.") + run_all_examples() + print("\nTry: n.base = 'new_value', p.get(version=12), f.suffix = 'txt', or pf.values") diff --git a/docs/source/_static/pyrepl/overview_extending.py b/docs/source/_static/pyrepl/overview_extending.py new file mode 100644 index 0000000..baceeb9 --- /dev/null +++ b/docs/source/_static/pyrepl/overview_extending.py @@ -0,0 +1,162 @@ +"""Interactive Overview examples for extending naming classes.""" + +from pprint import pprint + +from naming import File, PipeFile + + +class BasicPipeFile(PipeFile): + config = dict(base=r'\w+') + + +class ProjectFile(BasicPipeFile): + config = dict( + year='[0-9]{4}', + user='[a-z]+', + another='(constant)', + last='[a-zA-Z0-9]+', + ) + + +class Dropper(BasicPipeFile): + config = dict(without=r'[a-zA-Z0-9]+', basename=r'[a-zA-Z0-9]+') + drop = ('base',) + + +Subdropper = type('Dropper', (Dropper,), dict(config=dict(subdrop=r'[\w]'))) + + +class Compound(BasicPipeFile): + config = dict(first=r'\d+', second=r'[a-zA-Z]+') + join = dict(base=('first', 'second')) + + +class CompoundByDash(Compound): + join_sep = '-' + + +class FilePath(File): + config = dict(base=r'\w+', extrafield='[a-z0-9]+') + + def get_path_pattern_list(self): + # The path is solved from the same fields used in the name. + return super().get_pattern_list() + + +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 + + +def show(expression, value): + print(f">>> {expression}") + pprint(value) + + +def run_project_file_example(): + global project_file + + print("\nInheriting from an existing name") + print(">>> project_file = ProjectFile('project_data_name_2017_christianl_constant_iamlast.data.17.abc', sep='_')") + project_file = ProjectFile('project_data_name_2017_christianl_constant_iamlast.data.17.abc', sep='_') + show("project_file.values", project_file.values) + show("project_file.nice_name", project_file.nice_name) + show("project_file.year", project_file.year) + + print(">>> project_file.year = 'nondigits'") + try: + project_file.year = 'nondigits' + except ValueError as exc: + print(f"ValueError: {exc}") + + print(">>> project_file.year = 1907") + project_file.year = 1907 + show("project_file", project_file) + show("project_file.suffix", project_file.suffix) + + print(">>> project_file.sep = ' '") + project_file.sep = ' ' + show("project_file.name", project_file.name) + + +def run_dropper_example(): + global dropper, subdropper + + print("\nDropping fields from bases") + dropper = Dropper() + show("dropper.get()", dropper.get()) + + subdropper = Subdropper() + show("subdropper.get()", subdropper.get()) + + +def run_compound_example(): + global compound, compound_by_dash + + print("\nSetting compound fields") + compound = Compound() + show("compound.get()", compound.get()) + show("compound.get(first=50, second='abc')", compound.get(first=50, second='abc')) + + print(">>> compound.name = compound.get(base='101dalmatians', version=1, suffix='png')") + compound.name = compound.get(base='101dalmatians', version=1, suffix='png') + show("compound.nice_name", compound.nice_name) + show("compound.get(first=200)", compound.get(first=200)) + + print(">>> compound_by_dash = CompoundByDash('101-dalmatians.1.png')") + compound_by_dash = CompoundByDash('101-dalmatians.1.png') + show("compound_by_dash.get(first=300)", compound_by_dash.get(first=300)) + + +def run_file_path_example(): + global file_path + + print("\nDefining path rules for File subclasses") + file_path = FilePath() + show("file_path.get()", file_path.get()) + show("file_path.path", file_path.path) + + +def run_property_field_example(): + global property_field + + print("\nUsing properties as fields while solving names") + property_field = PropertyField() + show("property_field.get()", property_field.get()) + + print(">>> property_field.name = 'simple props staticvalue.1.abc'") + property_field.name = 'simple props staticvalue.1.abc' + show("property_field.values", property_field.values) + show("property_field.path", property_field.path) + + +def run_all_examples(): + run_project_file_example() + run_dropper_example() + run_compound_example() + run_file_path_example() + run_property_field_example() + + +def setup(): + print("Loaded ProjectFile, Dropper, Compound, FilePath, and PropertyField examples.") + print("The extension examples are running now; use the prompt afterward to experiment.") + run_all_examples() + print("\nTry: project_file.year = 2026, dropper.get(), compound.get(first=404), or property_field.values") diff --git a/docs/source/conf.py b/docs/source/conf.py index 06e18a8..d05ed3d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -151,7 +151,10 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] +html_static_path = ['_static'] +html_js_files = [ + 'https://cdn.jsdelivr.net/npm/pyrepl-web@0.3.0/dist/pyrepl.js', +] # -- Options for HTMLHelp output ------------------------------------------ From 2b364237df09cb0cf494fa0eb12c0de4f7275e6f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 15 Jun 2026 13:33:15 +0000 Subject: [PATCH 2/2] Constrain docs dependencies for Read the Docs Co-authored-by: chrizzftd --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d99230b..d7879fd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,4 +24,4 @@ packages = find: # docs dependencies install: # conda install --channel conda-forge pygraphviz # python -m pip install sphinx myst-parser sphinx-toggleprompt sphinx-copybutton sphinx-togglebutton sphinx-hoverxref sphinx_autodoc_typehints sphinx_rtd_theme -docs = sphinx; myst-parser; sphinx-toggleprompt; sphinx-copybutton; sphinx-togglebutton; sphinx-hoverxref; sphinx_autodoc_typehints; sphinx_rtd_theme +docs = sphinx<9; myst-parser; sphinx-toggleprompt; sphinx-copybutton; sphinx-togglebutton; sphinx-hoverxref; sphinx_autodoc_typehints<3.6; sphinx_rtd_theme