Skip to content

Commit

Permalink
Merge pull request #208 from wwkimball/development
Browse files Browse the repository at this point in the history
Prep 3.7.0 for release
  • Loading branch information
wwkimball authored Jan 15, 2023
2 parents fdbcc0e + 78aea7d commit 8fa2366
Show file tree
Hide file tree
Showing 36 changed files with 1,209 additions and 452 deletions.
1 change: 1 addition & 0 deletions .codacy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ exclude_paths:
- 'README.md'
- 'yamlpath/func.py' # Deprecated; the entire file is just relays
- 'yamlpath/patches/**' # 3rd Party contributions
- 'yamlpath/enums/pathseperators.py' # Legacy spelling compatibility
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ on:
jobs:
build:

runs-on: ubuntu-latest
runs-on: ubuntu-20.04 # Use ubuntu-latest when https://github.com/actions/setup-python/issues/544 is fixed
strategy:
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish-to-prod-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
jobs:
publish:
name: Publish to Production PyPI
runs-on: ubuntu-latest
runs-on: ubuntu-20.04 # Use ubuntu-latest when https://github.com/actions/setup-python/issues/544 is fixed
environment: 'PyPI: Production'

steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/python-publish-to-test-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ on:
jobs:
validate:
name: Code Quality Assessment
runs-on: ubuntu-latest
runs-on: ubuntu-20.04 # Use ubuntu-latest when https://github.com/actions/setup-python/issues/544 is fixed
strategy:
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ max-module-lines=1000
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
; no-space-check=trailing-comma,
; dict-separator

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
Expand Down
47 changes: 42 additions & 5 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
3.7.0
Enhancements:
* Support for Python 3.11 has been added.
* A new Search Keyword has been added, [unique([NAME])]. This operates against
collections to return only values which have no duplicates within the
collection; i.e.: [1, 2, 2, 3] has unique values, [1, 3]. The NAME argument
is required when operating against Hashes (maps/dicts) and Arrays-of-Hashes
(sequences/lists of maps/dicts) to identify which field/property to evaluate
for uniqueness. This can be inverted to return only results which are
duplicated within the collection.
* A new Search Keyword has been added, [distinct([NAME])]. This operates
against collections to return exactly one of every value within the
collection, discarding duplicates; i.e.: [1, 2, 2, 3] has distinct values,
[1, 2, 3]. The NAME argument is required when operating against Hashes
(maps/dicts) and Arrays-of-Hashes (sequences/lists of maps/dicts) to identify
which field/property to evaluate for uniqueness. This cannot be inverted.
* Added a new Collector Math Operator: & (intersection)
As in set mathematics, this yields only elements of two collections which are
common to both collections. Unlike set mathematics, collections allow
duplicate elements. If you need to enforce distinctness of the intersected
results, use the [distinct([NAME])] Search Keyword against the collected
result, as in `((list1)&(list2))[distinct([NAME])]`.

Bug Fixes:
* A typographical error in yamlpath.enums has been corrected with backward-
compatible adapters in place to support both the correct and incorrect
spelling of PathSeparators (formerly PathSeperators). If the PathSeperators
version appears in your own code, please update to the new spelling. The
incorrectly spelled version of this enumeration is now deprecated and will be
removed in a future release. Thanks go entirely to
https://github.com/AndydeCleyre for working so hard to submit this fix!
* Processor.get_nodes would emit a nonobvious error message when mustexist is
left at its default value (False) and yaml_path contained ** or * segments.
Now, these segment types are excluded from generating "missing" nodes; only
nodes which exist can be matched by * and **. Credit and my thanks go to
https://github.com/gdubicki for discovering and reporting this issue.

3.6.9:
Enhancements:
* Partial support for updating bare Python dict and Python's native
Expand Down Expand Up @@ -454,7 +491,7 @@ API Changes:
* YAMLPath instances now support nonmutating addition of individual segments
via the + operator. Whereas the append() method mutates the YAMLPath being
acted upon, + creates a new YAMLPath that is the original plus the new
segment. In both cases, the orignal YAMLPath's seperator is retained during
segment. In both cases, the orignal YAMLPath's separator is retained during
both operations. As with .append(), new segments added via + must also be
properly escaped -- typically via path.escape_path_section -- before being
added.
Expand Down Expand Up @@ -695,9 +732,9 @@ Enhancements:
tool, so a comprehensive treatise will be added to the project Wiki to
explore its capabilities. Along with those of its component classes, its
unit tests also provide many examples of the same.
* YAMLPath instances now support arbitrary changes to seperator.
* YAMLPath instances now support arbitrary changes to separator.
* YAMLPath instances now support equality testing (against the stored path),
immune to differences in seperator.
immune to differences in separator.
* The get_yaml_data function now supports "-" as a source file. This is
interpreted as a read from STDIN.
* Due to the change to the get_yaml_data function, the yaml-get reference
Expand Down Expand Up @@ -853,7 +890,7 @@ Enhancements:
* Added a new yaml-paths command-line tool. In short, it enables searching
YAML/Compatible files, returning YAML Paths for any matches. As an Alpha-
grade tool, it is being released at version 0.0.1. Feedback welcome!
* All command-line tools which accept --pathsep now accept symbolic seperators
* All command-line tools which accept --pathsep now accept symbolic separators
rather than only names; so, --pathsep=/ is idental to --pathsep=fslash, etc.
Minor changes were also made to all command-line tools to consolidate some
repeat code. Each has a version bump to reflect this minor refactoring
Expand Down Expand Up @@ -955,7 +992,7 @@ Enhancements:
* The separator used for identifying Hash sub-keys can now be customized. If
you prefer your paths to look like "/hash/sub/key" rather than "hash.sub.key",
you can now have it your way. For now, only . and / are allowed. The
seperator can be either strictly specified or automatically inferred by
separator can be either strictly specified or automatically inferred by
whether the first character of a given YAML Path is /. Command-line tools
like yaml-get and yaml-set have a new --pathsep argument for this; the default
is "auto" and can be set to "fslash" (/) or "dot" (.).
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ With an example like this, YAML Path enables:
`Some decrypted value, provided you have the appropriate EYAML keys`
* pass-through selections against arrays-of-hashes: `/users/roles` =
`["Writers"]\n["Power Users", "Editors"]` (each user's list of roles are a
seperate result)
separate result)
* collection of disparate results: `(/users/name)` =
`["User One", "User Two"]` (all names appear in a single result instead of
one per line)
Expand All @@ -144,7 +144,7 @@ For a deeper exploration of YAML Path's capabilities, please visit the

## Supported YAML Path Segments

A YAML Path *segment* is the text between seperators which identifies zero or
A YAML Path *segment* is the text between separators which identifies zero or
more parent or leaf nodes within the data structure. For dot-notation, a path
like `hash.key` identifies two segments: `hash` (a parent node) and `key` (a
leaf node). The same path in forward-slash notation would be: `/hash/key`.
Expand Down Expand Up @@ -235,17 +235,21 @@ YAML Path understands these segment types:
keywords are
[deeply explored on the Wiki](https://github.com/wwkimball/yamlpath/wiki/Search-Keywords)
and include:
* `[distinct(NAME)]`: Match exactly one of every value within collections,
discarding duplicates; i.e.: [1, 2, 2, 3] has distinct values, [1, 2, 3]
* `[has_child(NAME)]`: Match nodes having a named child key
* `[max([NAME])]`: Match nodes having the maximum value
* `[min([NAME])]`: Match nodes having the minimum value
* `[name()]`: Match only the name of the present node, discarding all
children
* `[parent([STEPS])]`, Step up 1-N levels in the document from the present
node
* `[unique(NAME)]`: Match only values which have no duplicates within
collections; i.e.: [1, 2, 2, 3] has unique values, [1, 3]
* Collectors: Placing any portion of the YAML Path within parenthesis defines a
virtual list collector, like `(YAML Path)`; concatenation and exclusion
operators are supported -- `+` and `-`, respectively -- along with nesting,
like `(...)-((...)+(...))`
virtual list collector, like `(YAML Path)`; concatenation, exclusion, and
intersection operators are supported -- `+`, `-`, and `&`, respectively --
along with nesting, like `(...)-((...)+(...))&(...)`
* Complex combinations:
`some::deep.hierarchy[with!=""].'any.valid'[.=~/(yaml|json)/][data%structure].or.complexity[4].2`
or `/some::deep/hierarchy[with!=""]/any*.*valid[.=~/(yaml|json)/][data%structure]/or/compl*xity[4]/2/**`
Expand All @@ -267,7 +271,7 @@ ephemeral or longer-lasting virtual Python environments.
### Requirements

This project requires [Python](https://www.python.org/) 3. It is rigorously
tested against Pythons 3.6 through 3.9. Most operating systems and
tested against Pythons 3.6 through 3.11. Most operating systems and
distributions have access to Python 3 even if only Python 2 -- or no Python, at
all -- came pre-installed. It is generally safe to have more than one version
of Python on your system at the same time, especially when using
Expand Down Expand Up @@ -331,7 +335,7 @@ pip3.7 install --upgrade setuptools

When you cannot or will not update `pip` or *setuptools*, just pre-install
*ruamel.yaml* and *python-dateutil* before yamlpath. Each must be installed
seperately and in order, like this (you **cannot** combine these installations
separately and in order, like this (you **cannot** combine these installations
into a single command):

```shell
Expand Down Expand Up @@ -473,7 +477,7 @@ optional arguments:
that differences exist -- when they do -- with an
exit-state of 1
-t ['.', '/', 'auto', 'dot', 'fslash'], --pathsep ['.', '/', 'auto', 'dot', 'fslash']
indicate which YAML Path seperator to use when
indicate which YAML Path separator to use when
rendering results; default=dot
-d, --debug output debugging details
-v, --verbose increase output verbosity
Expand Down Expand Up @@ -520,7 +524,7 @@ optional arguments:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-t ['.', '/', 'auto', 'dot', 'fslash'], --pathsep ['.', '/', 'auto', 'dot', 'fslash']
indicate which YAML Path seperator to use when
indicate which YAML Path separator to use when
rendering results; default=dot
-S, --nostdin Do not implicitly read from STDIN, even when YAML_FILE
is not set and the session is non-TTY
Expand Down Expand Up @@ -666,7 +670,7 @@ optional arguments:
child leaf nodes (see "reference handling options" for
restrictions)
-t ['.', '/', 'auto', 'dot', 'fslash'], --pathsep ['.', '/', 'auto', 'dot', 'fslash']
indicate which YAML Path seperator to use when
indicate which YAML Path separator to use when
rendering results; default=dot
-a, --refnames also search the names of &anchor and *alias references
-S, --nostdin Do not implicitly read from STDIN, even when there are
Expand Down Expand Up @@ -780,7 +784,7 @@ optional arguments:
-b, --backup save a backup YAML_FILE with an extra .bak file-
extension
-t ['.', '/', 'auto', 'dot', 'fslash'], --pathsep ['.', '/', 'auto', 'dot', 'fslash']
indicate which YAML Path seperator to use when rendering
indicate which YAML Path separator to use when rendering
results; default=dot
-M CHARS, --random-from CHARS
characters from which to build a value for --random;
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Operating System :: OS Independent",
"Environment :: Console",
"Topic :: Utilities",
Expand Down
20 changes: 10 additions & 10 deletions tests/test_commands_yaml_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from tests.conftest import create_temp_yaml_file

from yamlpath.enums import PathSeperators
from yamlpath.enums import PathSeparators

class Test_yaml_paths():
"""Tests the yaml-paths command-line interface."""
Expand Down Expand Up @@ -888,7 +888,7 @@ def test_expand_sequence_parents(self, script_runner, tmp_path_factory):
]) + "\n" == result.stdout

def test_yield_seq_children_direct(self, tmp_path_factory, quiet_logger):
from yamlpath.enums import PathSeperators, PathSearchMethods
from yamlpath.enums import PathSeparators, PathSearchMethods
from yamlpath.path import SearchTerms
from yamlpath.func import get_yaml_data, get_yaml_editor
from yamlpath.commands.yaml_paths import yield_children
Expand All @@ -908,7 +908,7 @@ def test_yield_seq_children_direct(self, tmp_path_factory, quiet_logger):
for assertion, path in zip_longest(assertions, yield_children(
quiet_logger, yaml_data,
SearchTerms(False, PathSearchMethods.EQUALS, "*", "value"),
PathSeperators.FSLASH, "", seen_anchors, search_anchors=True,
PathSeparators.FSLASH, "", seen_anchors, search_anchors=True,
include_aliases=False
)):
assert assertion == str(path)
Expand All @@ -918,7 +918,7 @@ def test_yield_seq_children_direct(self, tmp_path_factory, quiet_logger):
(True, ["/aliases[&aValue]", "/hash/key1", "/hash/key2", "/hash/key3"]),
])
def test_yield_map_children_direct(self, tmp_path_factory, quiet_logger, include_aliases, assertions):
from yamlpath.enums import PathSeperators, PathSearchMethods
from yamlpath.enums import PathSeparators, PathSearchMethods
from yamlpath.path import SearchTerms
from yamlpath.func import get_yaml_data, get_yaml_editor
from yamlpath.commands.yaml_paths import yield_children
Expand All @@ -941,13 +941,13 @@ def test_yield_map_children_direct(self, tmp_path_factory, quiet_logger, include
for assertion, path in zip_longest(assertions, yield_children(
quiet_logger, yaml_data,
SearchTerms(False, PathSearchMethods.EQUALS, "*", "anchor"),
PathSeperators.FSLASH, "", seen_anchors, search_anchors=True,
PathSeparators.FSLASH, "", seen_anchors, search_anchors=True,
include_value_aliases=include_aliases
)):
assert assertion == str(path)

def test_yield_raw_children_direct(self, tmp_path_factory, quiet_logger):
from yamlpath.enums import PathSeperators, PathSearchMethods
from yamlpath.enums import PathSeparators, PathSearchMethods
from yamlpath.path import SearchTerms
from yamlpath.func import get_yaml_data, get_yaml_editor
from yamlpath.commands.yaml_paths import yield_children
Expand All @@ -964,7 +964,7 @@ def test_yield_raw_children_direct(self, tmp_path_factory, quiet_logger):
for assertion, path in zip_longest(assertions, yield_children(
quiet_logger, yaml_data,
SearchTerms(False, PathSearchMethods.STARTS_WITH, "*", "some"),
PathSeperators.FSLASH, "", seen_anchors, search_anchors=False,
PathSeparators.FSLASH, "", seen_anchors, search_anchors=False,
include_key_aliases=False, include_value_aliases=False
)):
assert assertion == str(path)
Expand Down Expand Up @@ -1041,23 +1041,23 @@ def test_too_many_stdins(self, script_runner):
assert "Only one YAML_FILE may be the - pseudo-file" in result.stderr

@pytest.mark.parametrize("pathsep,output", [
(PathSeperators.AUTO, [
(PathSeparators.AUTO, [
'foo.x: 12',
'foo.y: hello world',
"foo.ip_range['initial']: 1.2.3.4",
'foo.ip_range[]: tba',
"foo.array['first']: Cluster1",
'array2[]: bar',
]),
(PathSeperators.DOT, [
(PathSeparators.DOT, [
'foo.x: 12',
'foo.y: hello world',
"foo.ip_range['initial']: 1.2.3.4",
'foo.ip_range[]: tba',
"foo.array['first']: Cluster1",
'array2[]: bar',
]),
(PathSeperators.FSLASH, [
(PathSeparators.FSLASH, [
'/foo/x: 12',
'/foo/y: hello world',
"/foo/ip_range['initial']: 1.2.3.4",
Expand Down
Loading

0 comments on commit 8fa2366

Please sign in to comment.