Skip to content

Commit

Permalink
Merge pull request #33 from todofixthis/develop
Browse files Browse the repository at this point in the history
Filters v3.4.0
  • Loading branch information
todofixthis authored Oct 6, 2023
2 parents 93faced + 5d81d7e commit de88e13
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 42 deletions.
34 changes: 29 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: CI

on:
push:
push: ~

jobs:
build:
Expand All @@ -12,20 +12,44 @@ jobs:
python-version:
# Note: Use quotes to avoid float cast - especially important if the
# version number ends with 0!
- "3.9"
- "3.10"
- "3.11"
- "3.12"

steps:
- name: Clone Repo
- name: Clone repo
uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
# https://github.com/actions/setup-python#caching-packages-dependencies
cache: pip
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
- name: Run Tests
- name: Run tests
run: python -m unittest

docs:
runs-on: ubuntu-latest

steps:
- name: Clone repo
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
# https://github.com/actions/setup-python#caching-packages-dependencies
cache: pip
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[docs-builder]
- name: Check docs build
run: |
cd docs
mkdir -p _static
make html
14 changes: 14 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# https://docs.readthedocs.io/en/stable/config-file/v2.html
version: 2

build:
os: ubuntu-22.04
tools:
python: "3.11"

python:
install:
- requirements: docs/requirements.txt

sphinx:
configuration: docs/conf.py
10 changes: 6 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. image:: https://github.com/todofixthis/filters/actions/workflows/build.yml/badge.svg
:target: https://github.com/todofixthis/filters/actions/workflows/build.yml
.. image:: https://readthedocs.org/projects/filters/badge/?version=latest
:target: http://filters.readthedocs.io/

Expand All @@ -14,8 +16,8 @@ data validation and processing pipelines, including:

And much more!

The output from one filter can be "piped" into the input of another, enabling
you to "chain" filters together to quickly and easily create complex data
The output from one filter can be piped into the input of another, enabling you
to chain filters together to quickly and easily create complex data schemas and
pipelines.


Expand Down Expand Up @@ -62,7 +64,7 @@ Parse a JSON string and check that it has correct structure:
f.FilterMapper(
{
'birthday': f.Date,
'gender': f.CaseFold | f.Choice(choices={'m', 'f', 'x'}),
'gender': f.CaseFold | f.Choice(choices={'f', 'm', 'n'}),
'utcOffset':
f.Decimal |
Expand All @@ -81,9 +83,9 @@ Requirements
------------
Filters is known to be compatible with the following Python versions:

- 3.12
- 3.11
- 3.10
- 3.9

.. note::
I'm only one person, so to keep from getting overwhelmed, I'm only committing
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ Requirements
------------
Filters is known to be compatible with the following Python versions:

* 3.12
* 3.11
* 3.10
* 3.9

.. note::
I'm only one person, so to keep from getting overwhelmed, I'm only committing
Expand Down
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html#id6
sphinx
sphinx_rtd_theme
5 changes: 0 additions & 5 deletions filters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
# http://stackoverflow.com/a/2073599/
from pkg_resources import require
__version__ = require('phx-filters')[0].version
del require

# Make the base filters accessible from the top level of the package.
# Note that the order is important here, due to dependencies.
from .base import *
Expand Down
10 changes: 7 additions & 3 deletions filters/extensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import typing
from importlib.metadata import EntryPoint, entry_points
from inspect import getmembers as get_members, isabstract as is_abstract, \
isclass as is_class, ismodule as is_module
from logging import getLogger

from class_registry import EntryPointClassRegistry
from pkg_resources import EntryPoint, iter_entry_points

from filters.base import BaseFilter

Expand Down Expand Up @@ -49,6 +49,11 @@ def __init__(self, group: str = GROUP_NAME) -> None:
super().__init__(group)

def __getattr__(self, item: str) -> typing.Type[BaseFilter]:
"""
Provides attr-like interface for accessing extension filters (the
default interface for class registries is to access items rather than
attributes).
"""
return self[item]

def __repr__(self):
Expand All @@ -58,8 +63,7 @@ def _get_cache(self) -> typing.Dict[str, typing.Type[BaseFilter]]:
if self._cache is None:
self._cache = {}

for target in iter_entry_points(
self.group): # type: EntryPoint
for target in entry_points(group=self.group): # type: EntryPoint
filter_ = target.load()

ift_result = is_filter_type(filter_)
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

[project]
name = "phx-filters"
version = "3.3.0"
version = "3.4.0"
description = "Validation and data pipelines made easy!"
readme = "README.rst"
requires-python = ">= 3.6"
requires-python = ">= 3.10"
license = { file = "LICENCE.txt" }
authors = [
{ email = "Phoenix Zerin <[email protected]>" }
Expand All @@ -23,16 +23,16 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
'Topic :: Text Processing :: Filters',
]

dependencies = [
"phx-class-registry",
"phx-class-registry >= 4.1.0",
"python-dateutil",
"pytz",
"regex >= 2018.8.17",
Expand Down
41 changes: 41 additions & 0 deletions test/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sys
from importlib.metadata import DistributionFinder, PathDistribution
from os import path
from pathlib import Path


class DummyDistributionFinder(DistributionFinder):
"""
Injects a dummy distribution into the meta path finder, so that we can
pretend like it's been pip installed during unit tests (i.e., so that we
can test ``FilterExtensionRegistry``), without polluting the persistent
virtualenv.
"""

DUMMY_PACKAGE_DIR = 'filter_extension.egg-info'

@classmethod
def install(cls):
for finder in sys.meta_path:
if isinstance(finder, cls):
# If we've already installed an instance of the class, then
# something is probably wrong with our tests.
raise ValueError(f'{cls.__name__} is already installed')

sys.meta_path.append(cls())

@classmethod
def uninstall(cls):
for i, finder in enumerate(sys.meta_path):
if isinstance(finder, cls):
sys.meta_path.pop(i)
return
else:
# If we didn't find an installed instance of the class, then
# something is probably wrong with our tests.
raise ValueError(f'{cls.__name__} was not installed')

def find_distributions(self, context=...) -> list[PathDistribution]:
return [PathDistribution(
Path(path.join(path.dirname(__file__), self.DUMMY_PACKAGE_DIR))
)]
19 changes: 7 additions & 12 deletions test/test_extensions.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
from os.path import dirname
from unittest import TestCase

from pkg_resources import working_set

from filters.extensions import FilterExtensionRegistry
from filters.macros import FilterMacroType
from test import TestFilterAlpha, TestFilterBravo
from test.helper import DummyDistributionFinder


def setUpModule():
#
# Install a fake distribution that we can use to inject entry
# points at runtime.
#
# The side effects from this are pretty severe, but they (very
# probably) only impact this test, and they are undone as soon as
# the process terminates.
#
working_set.add_entry(dirname(__file__))
# Inject a distribution that defines some entry points.
DummyDistributionFinder.install()


def tearDownModule():
DummyDistributionFinder.uninstall()


class FilterExtensionRegistryTestCase(TestCase):
Expand Down
14 changes: 7 additions & 7 deletions test/test_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ def test_fail_no_match(self):

def test_pass_unicode_character_class(self):
"""
By default, character classes like ``\w`` will take unicode into
By default, character classes like ``\\w`` will take unicode into
account.
"""
# "Hi, there!" in Japanese, according to the internet :innocent:
Expand Down Expand Up @@ -1382,7 +1382,7 @@ def test_pass_pattern_split(self):
You can also use a regex to split the string.
"""
self.assertFilterPasses(
self._filter('foo-12-bar-34-baz', pattern='[-\d]+'),
self._filter('foo-12-bar-34-baz', pattern=r'[-\d]+'),
['foo', 'bar', 'baz'],
)

Expand All @@ -1393,7 +1393,7 @@ def test_pass_pattern_split_with_groups(self):
"""
self.assertFilterPasses(
# Note grouping parentheses in the regex.
self._filter('foo-12-bar-34-baz', pattern='([-\d]+)'),
self._filter('foo-12-bar-34-baz', pattern=r'([-\d]+)'),
['foo', '-12-', 'bar', '-34-', 'baz'],
)

Expand All @@ -1405,7 +1405,7 @@ def test_pass_no_split(self):
parts.
"""
self.assertFilterPasses(
self._filter('foo:bar:baz', pattern='[-\d]+'),
self._filter('foo:bar:baz', pattern=r'[-\d]+'),
['foo:bar:baz'],
)

Expand Down Expand Up @@ -1659,7 +1659,7 @@ def test_pass_curly_hex(self):
"""
You can include curly braces around hex values.
Use ``Regex(r'^[\da-f]+$') | Uuid`` if you only want to allow plain
Use ``Regex(r'^[\\da-f]+$') | Uuid`` if you only want to allow plain
hex.
"""
filtered = self._filter('{54d6ebf8a3f55ed59becdedfb3b0773f}')
Expand All @@ -1678,7 +1678,7 @@ def test_pass_urn(self):
antiquated, but still valid.
If you want to prohibit URNs, chain this filter with
``Regex(r'^[\da-f]+$')``.
``Regex(r'^[\\da-f]+$')``.
References:
Expand Down Expand Up @@ -1764,7 +1764,7 @@ def test_pass_unicode(self):
"""
The incoming value is a unicode.
"""
self.assertFilterPasses('┻━┻︵ \(°□°)/ ︵ ┻━┻ ') # RAWR!
self.assertFilterPasses(r'┻━┻︵ \(°□°)/ ︵ ┻━┻ ') # RAWR!

def test_pass_bytes_utf8(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py3{11,10,9}
envlist = py3{12,11,10}

[testenv]
commands = python -m unittest

0 comments on commit de88e13

Please sign in to comment.