Add auto-formatting and linting

This commit is contained in:
Swen Kooij
2020-02-29 12:39:28 +02:00
parent 7e8027451e
commit 540e2962f7
8 changed files with 237 additions and 61 deletions
+1
View File
@@ -4,3 +4,4 @@ env/
*.pyc
__pycache__
*.egg-info
+7 -4
View File
@@ -1,8 +1,8 @@
import sys
from .setup_py_parser import parse_setup_py
from .requirements import RequirementsEditableEntry, RequirementsRecursiveEntry
from .requirements_txt_parser import parse_requirements_txt
from .requirements import RequirementsRecursiveEntry, RequirementsEditableEntry
from .setup_py_parser import parse_setup_py
def main() -> int:
@@ -11,13 +11,16 @@ def main() -> int:
aggregated_entries = []
for requirement in parse_requirements_txt(input_file):
if isinstance(requirement, RequirementsRecursiveEntry):
aggregated_entries.extend(list(parse_requirements_txt(requirement.path)))
aggregated_entries.extend(
list(parse_requirements_txt(requirement.path))
)
elif isinstance(requirement, RequirementsEditableEntry):
aggregated_entries.extend(list(parse_setup_py(requirement.path)))
else:
aggregated_entries.append(requirement)
print(aggregated_entries)
for req in aggregated_entries:
print(req.source.line, req.source.path)
return 0
+38 -46
View File
@@ -1,7 +1,7 @@
import os
from dataclasses import dataclass
from typing import Generator, Optional, List
from typing import Generator, List, Optional
@dataclass
@@ -40,83 +40,74 @@ class RequirementsPackageEntry(RequirementsEntry):
version: str
def parse_requirements(source: Optional[RequirementsEntrySource], lines: List[str]) -> Generator[RequirementsEntry, None, None]:
def parse_requirements(
source: Optional[RequirementsEntrySource], lines: List[str]
) -> Generator[RequirementsEntry, None, None]:
for index, line in enumerate(lines):
stripped_line = line.strip()
stripped_line = _clean_line(line)
if not len(stripped_line) or stripped_line.startswith('#'):
if not len(stripped_line) or stripped_line.startswith("#"):
continue
line_source = None
if source:
line_source = RequirementsEntrySource(
path=source.path,
line=line,
line_number=index + 1,
path=source.path, line=stripped_line, line_number=index + 1
)
if stripped_line.startswith('-r'):
yield parse_recursive_requirements_entry(
line_source,
stripped_line,
)
if stripped_line.startswith("-r"):
yield parse_recursive_requirements_entry(line_source, stripped_line)
elif stripped_line.startswith('-e'):
yield parse_editable_requirements_entry(
line_source,
stripped_line,
)
elif stripped_line.startswith("-e"):
yield parse_editable_requirements_entry(line_source, stripped_line)
# TODO: add support for other VCS's
elif stripped_line.startswith('git+'):
yield parse_vcs_requirements_entry(
line_source,
stripped_line,
)
elif stripped_line.startswith("git+"):
yield parse_vcs_requirements_entry(line_source, stripped_line)
else:
yield parse_package_requirements_entry(
line_source,
stripped_line,
)
yield parse_package_requirements_entry(line_source, stripped_line)
def parse_recursive_requirements_entry(source: RequirementsEntrySource, line: str) -> RequirementsRecursiveEntry:
path = line.replace('-r', '').strip()
def parse_recursive_requirements_entry(
source: RequirementsEntrySource, line: str
) -> RequirementsRecursiveEntry:
path = _clean_line(line.replace("-r", ""))
return RequirementsRecursiveEntry(
source=source,
path=os.path.join(os.path.dirname(source.path), path),
path=os.path.realpath(os.path.join(os.path.dirname(source.path), path)),
)
def parse_editable_requirements_entry(source: RequirementsEntrySource, line: str) -> RequirementsEditableEntry:
path = line.replace('-e', '').strip()
def parse_editable_requirements_entry(
source: RequirementsEntrySource, line: str
) -> RequirementsEditableEntry:
path = _clean_line(line.replace("-e", ""))
return RequirementsEditableEntry(
source=source,
path=os.path.join(os.path.dirname(source.path), path),
path=os.path.realpath(os.path.join(os.path.dirname(source.path), path)),
)
def parse_vcs_requirements_entry(source: RequirementsEntrySource, line: str) -> RequirementsVCSPackageEntry:
vcs_uri_split = line.split('+')
def parse_vcs_requirements_entry(
source: RequirementsEntrySource, line: str
) -> RequirementsVCSPackageEntry:
vcs_uri_split = line.split("+")
vcs = vcs_uri_split[0]
uri_tag_split = vcs_uri_split[1].split('#')
uri_tag_split = vcs_uri_split[1].split("#")
uri = uri_tag_split[0]
tag = None
if len(uri_tag_split) > 1:
tag = uri_tag_split[1]
return RequirementsVCSPackageEntry(
source=source,
vcs=vcs,
uri=uri,
tag=tag,
)
return RequirementsVCSPackageEntry(source=source, vcs=vcs, uri=uri, tag=tag)
def parse_package_requirements_entry(source: RequirementsEntrySource, line: str) -> RequirementsPackageEntry:
def parse_package_requirements_entry(
source: RequirementsEntrySource, line: str
) -> RequirementsPackageEntry:
operators = ["==", ">=", ">", "<=", "<"]
for operator in operators:
parts = line.split(operator)
@@ -124,8 +115,9 @@ def parse_package_requirements_entry(source: RequirementsEntrySource, line: str)
continue
return RequirementsPackageEntry(
source=source,
name=parts[0],
operator=operator,
version=parts[1],
source=source, name=parts[0], operator=operator, version=parts[1]
)
def _clean_line(line: str) -> str:
return line.strip().replace("\n", "").replace("\r", "")
+12 -4
View File
@@ -1,11 +1,19 @@
from typing import Generator
from .requirements import parse_requirements, RequirementsEntry, RequirementsEntrySource
from .requirements import (
RequirementsEntry,
RequirementsEntrySource,
parse_requirements,
)
def parse_requirements_txt(file_path: str) -> Generator[RequirementsEntry, None, None]:
source = RequirementsEntrySource(path=file_path, line=None, line_number=None)
def parse_requirements_txt(
file_path: str,
) -> Generator[RequirementsEntry, None, None]:
source = RequirementsEntrySource(
path=file_path, line=None, line_number=None
)
with open(file_path, 'r') as fp:
with open(file_path, "r") as fp:
for requirement in parse_requirements(source, fp.readlines()):
yield requirement
+14 -7
View File
@@ -1,9 +1,14 @@
import os
import setuptools
from typing import Generator
from .requirements import RequirementsEntry, RequirementsEntrySource, parse_requirements
import setuptools
from .requirements import (
RequirementsEntry,
RequirementsEntrySource,
parse_requirements,
)
def parse_setup_py(file_path: str) -> Generator[RequirementsEntry, None, None]:
@@ -11,18 +16,20 @@ def parse_setup_py(file_path: str) -> Generator[RequirementsEntry, None, None]:
def _setup_proxy(*args, **kwargs):
setup_kwargs.update(dict(kwargs))
setuptools.setup = _setup_proxy
path = os.path.join(file_path, "setup.py")
with open(path, "r") as fp:
exec(fp.read())
source = RequirementsEntrySource(
path=path,
line=None,
line_number=None,
)
source = RequirementsEntrySource(path=path, line=None, line_number=None)
requirements = setup_kwargs.get("install_requires") or []
extras_require = setup_kwargs.get("extras_require") or {}
for _, extra_requirements in extras_require.items():
requirements.extend(extra_requirements)
for requirement in parse_requirements(source, requirements):
yield requirement
+2
View File
@@ -0,0 +1,2 @@
[tool.black]
line-length = 80
+10
View File
@@ -0,0 +1,10 @@
[flake8]
ignore = E252,E501,W503
exclude = env,.tox,.git
[isort]
line_length=80
multi_line_output=3
lines_between_types=1
include_trailing_comma=True
not_skip=__init__.py
+153
View File
@@ -0,0 +1,153 @@
import distutils.cmd
import os
import subprocess
from setuptools import find_packages, setup
class BaseCommand(distutils.cmd.Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def create_command(text, commands):
"""Creates a custom setup.py command."""
class CustomCommand(BaseCommand):
description = text
def run(self):
for cmd in commands:
subprocess.check_call(cmd)
return CustomCommand
with open(
os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8"
) as readme:
README = readme.read()
setup(
name="pip-package-list",
version="1.0.0",
packages=find_packages(),
include_package_data=True,
license="MIT License",
description="Generate a flat list of packages Pip would install.",
long_description=README,
long_description_content_type="text/markdown",
url="https://github.com/Photonios/pip-package-list",
author="Swen Kooij",
author_email="swenkooij@gmail.com",
keywords=["pip", "package", "resolver", "list", "requirements"],
classifiers=[
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.5",
],
python_requires=">=3.7",
install_requires=["setuptools==45.2.0"],
extras_require={
"analysis": [
"black==19.10b0",
"flake8==3.7.7",
"autoflake==1.3",
"autopep8==1.4.4",
"isort==4.3.20",
"docformatter==1.3.1",
]
},
cmdclass={
"lint": create_command(
"Lints the code",
[["flake8", "setup.py", "pippackagelist", "tests"]],
),
"lint_fix": create_command(
"Lints the code",
[
[
"autoflake",
"--remove-all-unused-imports",
"-i",
"-r",
"setup.py",
"pippackagelist",
"tests",
],
["autopep8", "-i", "-r", "setup.py", "pippackagelist", "tests"],
],
),
"format": create_command(
"Formats the code",
[["black", "setup.py", "pippackagelist", "tests"]],
),
"format_verify": create_command(
"Checks if the code is auto-formatted",
[["black", "--check", "setup.py", "pippackagelist", "tests"]],
),
"format_docstrings": create_command(
"Auto-formats doc strings", [["docformatter", "-r", "-i", "."]]
),
"format_docstrings_verify": create_command(
"Verifies that doc strings are properly formatted",
[["docformatter", "-r", "-c", "."]],
),
"sort_imports": create_command(
"Automatically sorts imports",
[
["isort", "setup.py"],
["isort", "-rc", "pippackagelist"],
["isort", "-rc", "tests"],
],
),
"sort_imports_verify": create_command(
"Verifies all imports are properly sorted.",
[
["isort", "-c", "setup.py"],
["isort", "-c", "-rc", "pippackagelist"],
["isort", "-c", "-rc", "tests"],
],
),
"fix": create_command(
"Automatically format code and fix linting errors",
[
["python", "setup.py", "format"],
["python", "setup.py", "format_docstrings"],
["python", "setup.py", "sort_imports"],
["python", "setup.py", "lint_fix"],
],
),
"verify": create_command(
"Verifies whether the code is auto-formatted and has no linting errors",
[
["python", "setup.py", "format_verify"],
["python", "setup.py", "format_docstrings_verify"],
["python", "setup.py", "sort_imports_verify"],
["python", "setup.py", "lint"],
],
),
"test": create_command(
"Runs all the tests",
[
[
"pytest",
"--cov=pippackagelist",
"--cov-report=term",
"--cov-report=xml:reports/xml",
"--cov-report=html:reports/html",
"--junitxml=reports/junit/tests.xml",
"--reuse-db",
]
],
),
},
)