chore: Switch to nox, read config from python file

This commit is contained in:
Corentin Sechet 2022-03-28 16:22:18 +02:00
parent ff30346bbd
commit a389da81a4
9 changed files with 248 additions and 48 deletions

11
.flake8 Normal file
View File

@ -0,0 +1,11 @@
[flake8]
filename= marshpy tests
ignore =
# Line too long
E501
include = marshpy,test
max-complexity = 10
per-file-ignores =
# Imported but unused
__init__.py:F401

6
.gitignore vendored
View File

@ -1,8 +1,10 @@
**/__pycache__/**
**/__pycache__
*.egg-info
.mypy_cache
.nox
.pytest_cache
.ropeproject
.venv
build
poetry.lock
poetry.toml
test_config.py

View File

@ -0,0 +1,3 @@
""" Core module for Frontools"""
from .config import Config, SiteConfig

View File

@ -2,7 +2,6 @@
from asyncio import run
from functools import update_wrapper
from pathlib import Path
from sys import exit as sys_exit
from typing import Any, Optional
from click import Context
@ -29,14 +28,13 @@ def _async_command(function: Any) -> Any:
type=PathArgument(exists=True, file_okay=True, dir_okay=False),
default=None,
metavar="FILE",
help="Configuration file to use"
help="Configuration file to use",
)
def main(ctx: Context, config_file: Optional[Path]) -> None:
"""Utilities for EO frontend development.
"""
@_async_command
async def main(ctx: Context, config_file: Optional[Path]) -> None:
"""Utilities for EO frontend development."""
ctx.obj = load_config(config_file)
pass
ctx.obj = await load_config(config_file)
@main.command(name="css-diff")

View File

@ -1,36 +1,74 @@
"""Config loading"""
from gettext import gettext as _
from importlib.util import module_from_spec, spec_from_file_location
from inspect import isawaitable
from pathlib import Path
from typing import Optional
from typing import Iterable, Optional
from xdg import xdg_config_dirs
from marshpy import load, ANNOTATION_RESOLVER_CONFIG
class ConfigError(Exception):
pass
class Site:
class SiteConfig:
urls: list[str]
def __init__(self, urls: Iterable[str]):
self.urls = list(urls)
class Config:
sites: dict[str, Site] = {}
sites: dict[str, SiteConfig] = {}
def load_config(config_path: Optional[Path]) -> Config:
async def load_config(config_path: Optional[Path]) -> Config:
if config_path is None:
config_path = _find_config()
if config_path is None:
return Config()
with open(config_path, 'r', encoding='utf-8') as config_file:
return load(config_file, Config, config = [ANNOTATION_RESOLVER_CONFIG])
module_spec = spec_from_file_location("frontools.local_config", config_path)
assert module_spec is not None
config_module = module_from_spec(module_spec)
assert module_spec is not None
module_spec.loader.exec_module(config_module) # type: ignore
if not hasattr(config_module, "CONFIG"):
raise ConfigError(
_(f"Configuration file {config_path} should define a root variable CONFIG.")
)
config_provider = getattr(config_module, "CONFIG")
if callable(config_provider):
config_value = config_provider()
if isawaitable(config_value):
config = await config_value
else:
config = config_value
else:
config = config_provider
if not isinstance(config, Config):
raise ConfigError(
_(
f"The configuratio exported by {config_path} is not an instance of frontools.Config."
)
)
return config
def _find_config() -> Optional[Path]:
local_config = Path('.frontools.yaml')
local_config = Path(".frontools.py")
if local_config.is_file():
return local_config
for config_dir in xdg_config_dirs():
config_path = config_dir / 'frontools/config.yaml'
config_path = config_dir / "frontools/config.py"
if config_path.is_file():
return config_path

0
frontools/sources.py Normal file
View File

112
noxfile.py Normal file
View File

@ -0,0 +1,112 @@
"""Nox configuration file"""
from os import fsdecode
from pathlib import Path
import nox
from nox import Session
nox.options.sessions = ["checks"]
LINT_PATHS = ["frontools", "tests"]
PYTEST_PACKAGES = ["pytest", "pytest-cov", "pytest-datadir"]
DEV_DEPENDENCIES = PYTEST_PACKAGES + ["mypy", "pylint", "flake8", "isort", "poetry"]
VENV_DIR = Path(".venv")
@nox.session(python=["3.9", "3.10"], reuse_venv=True)
def tests(session: Session):
"""Run unit tests."""
session.install("-e", ".", *PYTEST_PACKAGES)
session.run("pytest")
@nox.session(reuse_venv=True)
def black(session: Session):
"""Check black formatting."""
session.install("black")
session.run("black", "--check", *LINT_PATHS)
@nox.session(reuse_venv=True)
def flake8(session: Session):
"""Run flake8"""
session.install("flake8")
session.run("flake8")
@nox.session(reuse_venv=True)
def isort(session: Session):
"""Check imports sorting"""
session.install("isort")
session.run("isort", "--check", *LINT_PATHS)
@nox.session(reuse_venv=True)
def mypy(session: Session):
"""Run Mypy"""
session.install("mypy", "types-PyYAML")
session.run("mypy", *LINT_PATHS)
@nox.session(reuse_venv=True)
def pylint(session: Session):
"""Run pylint"""
session.install("pylint", "-e", ".", *PYTEST_PACKAGES)
session.run("pylint", "--rcfile=pyproject.toml", *LINT_PATHS)
@nox.session(python=False)
def lint(session: Session):
"""Run all lint tasks"""
session.notify("black")
session.notify("flake8")
session.notify("isort")
session.notify("mypy")
session.notify("pylint")
@nox.session(python=False)
def checks(session: Session):
"""Run all checks"""
session.notify("lint")
session.notify("tests")
@nox.session(python=False)
def dev(session: Session) -> None:
"""
Sets up a python development environment for the project.
"""
session.run(
"python3",
"-m",
"virtualenv",
"-p",
"python3.10",
fsdecode(VENV_DIR),
silent=True,
)
python_path = Path.cwd() / VENV_DIR / "bin/python"
python = fsdecode(python_path)
session.run(
python,
"-m",
"pip",
"install",
"-e",
".",
*DEV_DEPENDENCIES,
external=True,
silent=True
)
@nox.session(python="3.10")
def publish(session: Session) -> None:
"""
Publish a package to pypi
"""
session.install("poetry", "poetry-dynamic-versioning")
session.run("poetry", "publish", "--build")

View File

@ -1,40 +1,81 @@
[build-system]
requires = ['poetry-core>=1.0.0', 'poetry-dynamic-versioning']
build-backend = 'poetry.core.masonry.api'
[tool.poetry]
name = "frontools"
version = "0.1.0"
description = "Frontend Automation"
authors = ["Corentin Séchet <csechet@entrouvert.com>"]
license = "WTFPL"
name = 'frontools'
version = '0.0.0'
description = 'Web QA tasks automation'
authors = ['Corentin Séchet <csechet@entrouvert.com>']
license = 'WTFPL'
readme = 'README.md'
repository = 'https://git.entrouvert.org/frontools'
keywords = ['web', 'QA', 'Tests' ]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
]
packages = [
{ include = 'frontools/**/*' },
]
[tool.poetry.dependencies]
python = "^3.9"
click = "^8.0.4"
playwright = "^1.20.0"
python= ">3.7,<4.0"
bs4 = "^0.0.1"
xdg = "^5.1.1"
beautifulsoup4 = "^4.10.0"
PyYAML = "^6.0"
playwright = "^1.20.1"
[tool.poetry.dev-dependencies]
pylint = "^2.12.2"
pytest = "^7.1.1"
mypy = "^0.941"
isort = "^5.10.1"
black = "^22.1.0"
mypy = "^0.942"
pylint = "^2.13.2"
[tool.poetry.scripts]
frontools = 'frontools.cli:main'
[tool.poetry-dynamic-versioning]
enable = true
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.pytest.ini_options]
junit_family = "legacy"
addopts = "--cov=frontools --cov-report html"
testpaths = "tests"
[tool.pycodestyle]
ignore = ["E501"]
[tool.mypy]
[[tool.mypy.overrides]]
module = [
'pytest',
'nox',
'xdg',
'bs4',
'playwright.async_api'
]
ignore_missing_imports = true
strict = true
[tool.isort]
profile = "black"
[tool.pylint.messages_control]
disable = [
"line-too-long", # Already checked by pep8
"unsubscriptable-object", # https://github.com/PyCQA/pylint/issues/3882
"invalid-name",
"too-few-public-methods",
"line-too-long", # Already checked by black
"too-many-arguments",
]
[tool.pylint.similaritie]
ignore-imports=true
ignore-signatures=true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:"
]

View File

@ -1,5 +0,0 @@
sites:
auch:
urls:
- http://demarches.auch.fr