Skip to content

Commit

Permalink
Switch to hatchling based build
Browse files Browse the repository at this point in the history
  • Loading branch information
icgood committed Feb 25, 2023
1 parent c1dd494 commit e458892
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 96 deletions.
6 changes: 6 additions & 0 deletions .flake8
@@ -0,0 +1,6 @@
[flake8]
extend-select = B901, B902, B903, B904
extend-ignore = B028, ANN101, ANN102
per-file-ignores =
test/*:ANN
tasks/*:ANN
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Expand Up @@ -23,7 +23,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install build tools
run: |
python -m pip install --upgrade pip setuptools wheel invoke coveralls
python -m pip install --upgrade pip invoke coveralls
- name: Install package and dependencies
run: |
invoke install
Expand All @@ -49,7 +49,7 @@ jobs:
python-version: '3.11'
- name: Install build tools
run: |
python -m pip install --upgrade pip setuptools wheel invoke
python -m pip install --upgrade pip invoke
- name: Build the Sphinx documentation
run: |
invoke install doc.install doc.build
6 changes: 3 additions & 3 deletions .github/workflows/python-publish.yml
Expand Up @@ -17,13 +17,13 @@ jobs:
python-version: '3.11'
- name: Install build tools
run: |
python -m pip install --upgrade pip setuptools wheel twine
python -m pip install --upgrade pip build twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
python -m build
twine upload dist/*
docs:
Expand All @@ -38,7 +38,7 @@ jobs:
python-version: '3.11'
- name: Install build tools
run: |
python -m pip install --upgrade pip setuptools wheel invoke
python -m pip install --upgrade pip invoke
- name: Build the Sphinx documentation
run: |
invoke install doc.install doc.build
Expand Down
2 changes: 0 additions & 2 deletions MANIFEST.in

This file was deleted.

4 changes: 2 additions & 2 deletions doc/source/conf.py
Expand Up @@ -14,7 +14,7 @@
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import pkg_resources
from importlib.metadata import distribution

import cloud_sptheme as csp # type: ignore

Expand All @@ -26,7 +26,7 @@
author = 'Ian Good'

# The short X.Y version
project_version = pkg_resources.require(project)[0].version
project_version = distribution(project).version
version_parts = project_version.split('.')
version = '.'.join(version_parts[0:2])
# The full version, including alpha/beta/rc tags
Expand Down
21 changes: 21 additions & 0 deletions importlib_metadata.pyi
@@ -0,0 +1,21 @@

from typing import Any, Iterable, NamedTuple


class Distribution(NamedTuple):
version: str


def distribution(name: str) -> Distribution:
...


class EntryPoint:
name: str

def load(self) -> Any:
...


def entry_points(*, group: str) -> Iterable[EntryPoint]:
...
69 changes: 67 additions & 2 deletions pyproject.toml
@@ -1,12 +1,77 @@
# Copyright (c) 2023 Ian C. Good
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

[build-system]
requires = ['setuptools', 'wheel']
requires = ['hatchling']
build-backend = 'hatchling.build'

[project]
name = 'pysasl'
version = '1.0.1'
authors = [
{ name = 'Ian Good', email = 'ian@icgood.net' },
]
description = 'Pure Python SASL client and server library.'
license = { file = 'LICENSE.md' }
readme = { file = 'README.md', content-type = 'text/markdown' }
requires-python = '~=3.7'
classifiers = [
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
dependencies = [
'typing-extensions',
'importlib-metadata; python_version < "3.10"',
]

[project.optional-dependencies]
hashing = ['passlib']

[project.urls]
homepage = 'https://github.com/icgood/pysasl/'

[project.entry-points.'pysasl.mechanism']
CRAM-MD5 = 'pysasl.mechanism.crammd5:CramMD5Mechanism'
EXTERNAL = 'pysasl.mechanism.external:ExternalMechanism'
LOGIN = 'pysasl.mechanism.login:LoginMechanism'
PLAIN = 'pysasl.mechanism.plain:PlainMechanism'
XOAUTH2 = 'pysasl.mechanism.oauth:OAuth2Mechanism'

[tool.hatch.build]
exclude = ['/tasks', '/doc', '/.github']

[tool.mypy]
strict = true
files = ['pysasl', 'test']

[[tool.mypy.overrides]]
module = 'passlib.*'
module = 'importlib_metadata.*'
ignore_missing_imports = true

[tool.bandit]
Expand Down
20 changes: 12 additions & 8 deletions pysasl/__init__.py
@@ -1,9 +1,13 @@

import sys
from collections import OrderedDict
from typing import Optional, Iterable, Sequence
from typing_extensions import Final
from typing import Iterable, Optional, Sequence
from typing_extensions import Final, Self

import pkg_resources
if sys.version_info >= (3, 10): # pragma: no cover
from importlib.metadata import distribution, entry_points
else: # pragma: no cover
from importlib_metadata import distribution, entry_points

from . import mechanism
from .config import default_config, SASLConfig
Expand All @@ -12,7 +16,7 @@
__all__ = ['__version__', 'SASLAuth']

#: The pysasl package version.
__version__: str = pkg_resources.require(__package__)[0].version
__version__: str = distribution(__package__).version


class SASLAuth(SASLConfig):
Expand All @@ -38,7 +42,7 @@ def __init__(self, mechanisms: Sequence[Mechanism], *,
for mech in mechanisms if isinstance(mech, ClientMechanism))

@classmethod
def defaults(cls, *, config: SASLConfig = default_config) -> 'SASLAuth':
def defaults(cls, *, config: SASLConfig = default_config) -> Self:
"""Uses the default built-in authentication mechanisms, ``PLAIN`` and
``LOGIN``.
Expand All @@ -53,7 +57,7 @@ def defaults(cls, *, config: SASLConfig = default_config) -> 'SASLAuth':

@classmethod
def named(cls, names: Iterable[bytes], *,
config: SASLConfig = default_config) -> 'SASLAuth':
config: SASLConfig = default_config) -> Self:
"""Uses the built-in authentication mechanisms that match a provided
name.
Expand All @@ -69,13 +73,13 @@ def named(cls, names: Iterable[bytes], *,
"""
builtin = {m.name: m for m in cls._get_builtin_mechanisms(config)}
return SASLAuth([builtin[name] for name in names])
return cls([builtin[name] for name in names])

@classmethod
def _get_builtin_mechanisms(cls, config: SASLConfig) \
-> Iterable[Mechanism]:
group = mechanism.__package__
for entry_point in pkg_resources.iter_entry_points(group):
for entry_point in entry_points(group=group):
mech_cls = entry_point.load()
yield mech_cls(entry_point.name, config)

Expand Down
9 changes: 4 additions & 5 deletions pysasl/hashing.py
Expand Up @@ -14,8 +14,9 @@
try:
from passlib.context import CryptContext
except ImportError as _exc: # pragma: no cover
CryptContext = None
_passlib_import_exc = _exc
_passlib_import_exc: Optional[ImportError] = _exc
else:
_passlib_import_exc = None

__all__ = ['HashT', 'HashInterface', 'BuiltinHash', 'Cleartext', 'get_hash']

Expand All @@ -31,8 +32,6 @@ class HashInterface(Protocol):
"""

__slots__: Sequence[str] = []

@abstractmethod
def copy(self: HashT, **kwargs: Any) -> HashT:
"""Return a copy of the hash implementation. The *kwargs* may be used
Expand Down Expand Up @@ -227,7 +226,7 @@ def get_hash(*, passlib_config: Optional[str] = None) \
"""
context: HashInterface
if passlib_config is not None:
if CryptContext is None:
if _passlib_import_exc is not None:
raise _passlib_import_exc
context = CryptContext.from_path(passlib_config)
elif CryptContext is not None:
Expand Down
4 changes: 2 additions & 2 deletions pysasl/identity.py
Expand Up @@ -2,7 +2,7 @@
import secrets
from abc import abstractmethod
from typing import Optional, Sequence
from typing_extensions import Protocol
from typing_extensions import Protocol, Self

from .hashing import HashInterface, Cleartext
from .prep import default_prep, Preparation
Expand Down Expand Up @@ -104,7 +104,7 @@ def __init__(self, authcid: str, digest: str, *,
@classmethod
def create(cls, authcid: str, secret: str, *,
hash: HashInterface,
prepare: Preparation = default_prep) -> 'HashedIdentity':
prepare: Preparation = default_prep) -> Self:
"""Prepare and hash the given *secret*, returning a
:class:`HashedIdentity`.
Expand Down
16 changes: 8 additions & 8 deletions pysasl/prep.py
@@ -1,14 +1,17 @@

import warnings
from abc import abstractmethod
from typing import Optional
from typing_extensions import Final, Protocol

try:
from passlib.utils import saslprep as _saslprep
except ImportError as exc: # pragma: no cover
_saslprep = None
_saslprep_exc = exc
_saslprep_exc: Optional[ImportError] = exc
warnings.warn('passlib.utils.saslprep is not available', ImportWarning)
else:
_saslprep_exc = None


__all__ = ['Preparation', 'default_prep', 'noprep', 'saslprep']

Expand Down Expand Up @@ -51,14 +54,11 @@ def saslprep(source: str) -> str:
ImportError: The implementation is not available.
"""
if _saslprep is not None:
ret: str = _saslprep(source)
return ret
else: # pragma: no cover
raise _saslprep_exc
ret: str = _saslprep(source)
return ret


if _saslprep is not None:
if _saslprep_exc is None:
_default_prep = saslprep
else: # pragma: no cover
_default_prep = noprep
Expand Down
4 changes: 3 additions & 1 deletion requirements-dev.txt
@@ -1,13 +1,15 @@
invoke
build
pytest
pytest-cov
flake8
flake8-annotations
flake8-bugbear
pyright
bandit[toml]
mypy
rope

types-setuptools
types-passlib

-r requirements-all.txt
60 changes: 0 additions & 60 deletions setup.py

This file was deleted.

0 comments on commit e458892

Please sign in to comment.