Code Monkey home page Code Monkey logo

pyprojectsort's People

Contributors

dariazvereva avatar dependabot[bot] avatar kieran-ryan avatar pre-commit-ci[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

dariazvereva

pyprojectsort's Issues

Writing back normalised keys causes issues for tools expected dashes

pyprojectsort normalises dashes in key and section names to underscores. While this is useful from the point of view of needing to read the configuration for internal use within an application, by writing it back to the file, we are modifying keys expected by various tools, breaking compatibility with those tools.

Expected Result

Outputted pyproject keeps dash seperators in keys:

[build-system]
build-backend = "flit.buildapi"
requires = [
    "flit",
]

Actual Result

Outputted pyproject keeps dash seperators in keys:

[build_system]
build_backend = "flit.buildapi"
requires = [
    "flit",
]

Reproduction Steps

Run pyproject:

pyprojectsort

against a pyproject.toml containing the following:

[build_system]
build-backend = "flit.buildapi"
requires = [
    "flit",
]

Command line option to check whether pyproject.toml would be reformatted

Like other Python formatters such as black and isort, having a command line option to check whether formatting would be applied - without actually applying formatting - would be useful for linting purposes, such as in a continuous integration pipeline, pre-commit git hook, or to check locally from the command line whether it is formatted correctly.

pyprojectsort --check

Command line option to render a diff of what would be reformatted

While a command line option could be used by a user to check whether their pyproject.toml will be reformatted (#10), it will not allow them to see the actual changes that would be made. New users might want to check how the package will reformat their pyproject file before deciding whether to adopt it, developers accustomed to a command line interface may simply want to be aware of what changes would be made; and for continuous integration pipelines and linting purposes, it could be advantageous to have traceability on what would be changed to understand how the failing code may have entered the pipeline.

Example usage:

pyprojectsort --diff

Reformatting is not performed unless list values are unsorted

Reformatting is performed if the original pyproject is not equivalent to the modified pyproject. As these are read as a dictionary and their equivalence checked, their equivalence only evaluates to False when lists are unsorted; as for example ["a", "b"] == ["b", "a"] evaluates to False.

Expected Result

pyproject is reformatted if its format post-modification by pyprojectsort, differs from the original.

Actual Result

pyproject is not reformatted unless there is a change in list value order.

Reproduction Steps

Create a pyproject.toml as follows:

[build-system]
requires = ["flit", "z"]

Run pyprojectsort against it:

pyprojectsort

The pyproject.toml is not reformatted and a 'left unchanged' message is displayed.

Then, swap the list order in a pyproject.toml as follows:

[build-system]
requires = ["z", "flit"]

Run pyprojectsort against it:

pyprojectsort

The pyproject.toml is reformatted as follows:

[build-system]
requires = [
    "flit",
    "z",
]

Support package distribution through Anaconda

Alternatively to pip and PyPI, Anaconda is a popular choice for the distribution of Python packages: aimed at scientific computing, and to simplify package management and deployment.

Thus it would be useful for pyprojectsort to extend support to this user base and be installable through conda (via conda-forge) as follows:

conda install -c conda-forge pyprojectsort

Project dependencies with names matching up to a dash are not alphabetically sorted

When the project dependencies are sorted, they are done so alphabetically with the full content of the list. This includes operators. As a result, when we have the same package name for two packages, but one is extended with a dash, they end up in reverse order as '-' is compared against and precedes '=' alphabetically.

To resolve this issue, the logic would need to be modified to specifically check package names only and not the operators they are using to check versions, etc.

Expected Result

[project]
dependencies = [
    "tomli==2.0.1",
    "tomli-w==1.0.0",
]

Actual Result

[project]
dependencies = [
    "tomli-w==1.0.0",
    "tomli==2.0.1",
]

Reproduction Steps

pyprojectsort

Alphabetically sort by section key

The keys of pyproject.toml sections are highly susceptible to change as a project matures with time. They are thus difficult to keep aligned across developers and can be a source of merge conflicts. By alphabetically sorting these keys, a standardised formatting can be applied that will ensure the output of each developer is exactly the same.

With an example of the following, unsorted section:

[tool.mypy]
mypy_path = "pyprojectsort"
exclude = "__init__.py|docs|tests|venv"
files = "."

The expect output post-formatting would be the alphabetically sorted section below:

[tool.mypy]
exclude = "__init__.py|docs|tests|venv"
files = "."
mypy_path = "pyprojectsort"

Integrate pre-commit CI to automate hook version updates and fixing trivial issues

Referencing the pre-commit CI documentation:

Developers spend a fair chunk of time during their development flow on fixing relatively trivial problems in their code. pre-commit.ci both enforces that these issues are discovered (which is opt-in for each developer's workflow via pre-commit) but also fixes the issues automatically, letting developers focus their time on more valuable problems.

It is clear that there is a significant benefit in automatically fixing trivial issues that interrupt the software development lifecycle and also in keeping git hooks up to date. While dependabot automatically updates most package dependency files (such as requirements files for Python), it does not currently support updating pre-commit hook versions; thus, this tool covers that important and tedious use case.

Alphabetically sort lists by value

List values in pyproject.sort can grow significantly, particularly with larger projects (see home-assistant). These lists are typically kept in a logicial, alphabetical order for consistency and to ensure ease of reference. This presents a maintenance challenge for the project.

With the following configuration:

[tool.ruff]
ignore = [
    "T201",
    "G004",
    "D203",
    "ARG",
    "INP001",
    "DTZ005",
    "ANN",
]

Automation could alphabetically sort the the configuration as follows:

[tool.ruff]
ignore = [
    "ANN",
    "ARG",
    "DTZ005",
    "D203",
    "G004",
    "INP001",
    "T201",
]

Package not building due to invalid absolute import

Package build process is failing due to an invalid absolute import.

Expected Result

pyprojectsort to build and deploy to PyPI.

Actual Result

python -m build
[23](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:24)
* Creating venv isolated environment...
[24](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:25)
* Installing packages in isolated environment... (flit)
[25](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:26)
* Getting build dependencies for sdist...
[26](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:27)
* Installing packages in isolated environment... (tomli-w==1.0.0, tomli==2.0.1)
[27](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:28)
* Building sdist...
[28](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:29)
Traceback (most recent call last):
[29](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:30)
  File "/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/pyproject_hooks/_in_process/_in_process.py", line 353, in <module>
[30](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:31)
    main()
[31](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:32)
  File "/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/pyproject_hooks/_in_process/_in_process.py", line 335, in main
[32](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:33)
    json_out['return_val'] = hook(**hook_input['kwargs'])
[33](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:34)
  File "/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/pyproject_hooks/_in_process/_in_process.py", line 304, in build_sdist
[34](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:35)
    return backend.build_sdist(sdist_directory, config_settings)
[35](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:36)
  File "/tmp/build-env-51skd9k7/lib/python3.10/site-packages/flit_core/buildapi.py", line 82, in build_sdist
[36](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:37)
    path = SdistBuilder.from_ini_path(pyproj_toml).build(Path(sdist_directory))
[37](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:38)
  File "/tmp/build-env-51skd9k7/lib/python3.10/site-packages/flit_core/sdist.py", line 93, in from_ini_path
[38](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:39)
    metadata = common.make_metadata(module, ini_info)
[39](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:40)
  File "/tmp/build-env-51skd9k7/lib/python3.10/site-packages/flit_core/common.py", line 425, in make_metadata
[40](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:41)
    md_dict.update(get_info_from_module(module, ini_info.dynamic_metadata))
[41](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:42)
  File "/tmp/build-env-51skd9k7/lib/python3.10/site-packages/flit_core/common.py", line 222, in get_info_from_module
[42](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:43)
    docstring, version = get_docstring_and_version_via_import(target)
[43](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:44)
  File "/tmp/build-env-51skd9k7/lib/python3.10/site-packages/flit_core/common.py", line 195, in get_docstring_and_version_via_import
[44](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:45)
    spec.loader.exec_module(m)
[45](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:46)
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
[46](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:47)
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
[47](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:48)
  File "/home/runner/work/pyprojectsort/pyprojectsort/pyprojectsort/__init__.py", line 5, in <module>
[48](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:49)
    from .main import reformat_pyproject
[49](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:50)
  File "/home/runner/work/pyprojectsort/pyprojectsort/pyprojectsort/main.py", line 12, in <module>
[50](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:51)
    from pyprojectsort import __version__
[51](https://github.com/kieran-ryan/pyprojectsort/actions/runs/5467369865/jobs/9953581985#step:5:52)
ModuleNotFoundError: No module named 'pyprojectsort'

Reproduction Steps

Within pyprojectsort's repository, run:

python -m build

Write to file on reformat only

Currently, the pyproject.toml is rewritten to whether any reformatting would be applied or not. This is inefficient as unnecessary io write operations performed when there are no reformatting changes, but the package writes the original contents back to the file. As such, it would beneficial to rectify this and first check whether reformatting would occur, and to secondly only write to the file if there are changes to reformat.

Spellcheck support for pyprojectsort source code in pipelines

To ensure spelling can be searched across a repository as expected and that all text is readable, a spell checker (such as CSpell can be employed; ensuring all words match the checker's dictionary. The alternative of checking all contributions manually is error-prone and effort-intensive.

A spell-checker can be introduced to a pipeline, git hook, or IDE; which in their respective order, facilitate an increasing shift-left in the detection of spelling mistakes.

Run pyprojectsort against its own pyproject.toml

Now that pyprojectsort provides a --check command line option (#10), its own pyproject.toml file can be reformatted and thereafter validated within a pipeline without actually performing the reformatting of the file itself - which can affect other jobs if not using a clean repo clone for each task.

This will allow the project to realise the benefit of its own package and to assist in identifying opportunities for enhancement.

Test coverage README badge

When selecting dependencies to use in a project, certain criteria are typically examined to determine suitability, security, and levels of maintenance and reliability. An important aspect in terms of reliability - and 'will this tool work as expected' - is the test coverage: high test coverage indicating well-tested, maintained software and low coverage indicating otherwise.

It is thus important to make the test coverage highly visible and easily accessible. One of the best ways to do this is to output your test coverage to a file or logs and have it be detected and rendered for display in a markdown badge at the top of a project's README file.

For example:

cov

Could additionally upload a JUnit report for test report summary information in merge requests.

Project documentation not deployed on release

The project documentation is rendered through a 'gh-pages' branch using GitHub Pages. The pages deployment job checks whether the GitHub event is a release so it doesn't deploy on every push or PR; however, the GitHub workflow specification that the documentation build job sits in only runs on push and PRs, so the deployment never runs.

Expected Result

Project release documentation is deployed to the 'gh-pages' branch when a release is created.

Actual Result

Project release documentation is not deployed to the 'gh-pages' branch when a release is created.

Reproduction Steps

Create a new project release.

Tox support for executing tests against package builds with supported Python distributions

Rather than executing tests against development source code locally, tox can be used to execute tests against package distributions - building them prior to testing - against each supported Python distribution.

This makes it very quick and easy to test builds locally, rather than development code; and can also be executed in the same way in the GitHub workflow - ensuring builds are thoroughly tested locally and through continuous integration with the same builds and test automation tooling.

Overall, this will increase package robustness, testability and development workflow.

An example tox.ini might appear as follows:

[tox]
env_list =
    py37
    py38
    py39
    py310
    py311
minversion = 4.6.4

[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
deps =
    pytest>=6
commands =
    pytest {tty:--color=yes} {posargs}

Split package build and deployment into separate tasks in the pipeline

The project is currently packaged and published in each of the deployment jobs: to PyPI and TestPyPI. This slows down the pipeline as the package build process runs twice. As the project is only built on release - when the package would be deployed - build failures are not detected in the pipeline until a release is created.

To resolve these issues, the build process can be extracted from the deployment tasks into its own individual task, of which other tasks can use its distribution.

Extend officially supported Python versions

The package currently only officially supports Python 3.10, however efforts have been made to ensure backwards compatibility:

  • tomliis being used as a dependency and aliased to tomllib to allow versions prior to 3.11 to have TOML support with a matching interface to that version onwards
  • from __future__ import annotations is being applied to use type-hints of 3.11 onwards in earlier Python versions

By configuring a test environment setup that runs and tests the package against Python 3.8-3.11 (3.7 having reached end-of-life on the 27th of June 2023; and 3.12 not due for initial release until the 2nd of October 2023), the package can indicate official support for the full range of actively developed Python distributions which will make adoption easier. Additionally, for the supported versions badge in README.md, it would be advantageous to pull the supported versions directly from PyPI to ensure a source of truth and to automate that information being updated in the badge rather than having to do so manually.

Implement pre-commit support

While it is trivial to manually run pyprojectsort from the command line against a pyproject.toml file (e.g. simply pyprojectsort within the same directory), it is a task developers must remember to do with each committed change to the file and thus is susceptible to follow-on cleanup work where it is missed. To automate this process and ensure every committed change has been formatted with pyprojectsort, a pre-commit git hook can be implemented.

The below is an example of how the tool might be specified inside a .pre-commit-config.yaml pre-commit configuration file.

  - repo: https://github.com/kieran-ryan/pyprojectsort
    rev: v0.1.1
    hooks:
      - id: pyprojectsort

Add a contributing guide

A CONTRIBUTING.md file is an important element of every repository. It provides visitors with an overview of how they can set up, work with and contribute to the repository. Writing this documentation, also provides repository owners with a useful opportunity to assess usability issues and to examine the project from a fresh perspective, with a mindset of 'What issues might someone seeing this for the first time encounter?' or 'Is there any way I could make this implementation more explicit and reduce its "unknown unknowns"?'.

This will inform anyone looking to contribute to the project how to act with autonomy and to not be dependant on direct support and answers from the original author.

Unable to sort an array containing dictionaries or mixed data types

The inbuilt sorted command in Python is unable to do dictionary and mixed data type comparisons to determine alphabetical ordering in an array. Matching floats and integer values are also kept in their original order by sorted (e.g. [5.0, 5] sorts to [5.0, 5] and [5, 5.0] sorts to [5, 5.0]). A solution is required to handle these cases and to also alphabetically order the contents of the dictionaries, and the order of each of those dictionaries within the array based on their keys and values.

Expected Result

An array of dictionaries and mixed data types are alphabetically sorted.

Actual Result

A TypeError was raised that '<' not supported between instances of 'dict' and 'dict' when trying to sort the array containing those dictionaries.

Traceback (most recent call last):
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/bin/pyprojectsort", line 8, in <module>
    sys.exit(main())
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 85, in main
    reformatted_pyproject: dict = reformat_pyproject(pyproject)
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 58, in reformat_pyproject
    return {
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 59, in <dictcomp>
    key: reformat_pyproject(value)
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 58, in reformat_pyproject
    return {
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 59, in <dictcomp>
    key: reformat_pyproject(value)
  File "/Users/KieranRyan/projects/python/pyproject-sort/venv/lib/python3.10/site-packages/pyprojectsort/main.py", line 63, in reformat_pyproject
    return sorted(reformat_pyproject(item) for item in pyproject)
TypeError: '<' not supported between instances of 'dict' and 'dict'

Reproduction Steps

Create a pyproject.toml file as follows:

[project]
authors = [
    { name = "Kieran Ryan" },
    { name = "Author Name" },
]

Execute pyprojectsort:

pyprojectsort

Migrate project documentation from Sphinx to Material for Mkdocs

Material for Mkdocs delivers a clean aesthetic for generating static-site project documentation - adhering to the well-established Material design system.

The project's current documentation is generated through Sphinx, using the furo theme. While this is satisfactory, the markdown-first approach of Material for Mkdocs is desirable - though markdown can be enabled with Sphinx. Also a feature of Sphinx, generating documentation from Python docstrings is also possible with mkdocstrings.

With the limited scope of this project, some of the extensibility and rich ecosystem of Sphinx are unlikely to be necessary, and delivering improved documentation through Material for Mkdocs may be the desired approach.

This work may also offer the opportunity to significantly expand the project's documentation and provide detailed examples of individual formatting changes that are made by the library. The docs could be generated in their own separate pipeline yaml and its status displayed in a README badge.

Alphanumerical sort does not account for natural numerical ordering in strings

Due to alphanumerical ordering not respecting all natural numerical patterns when included in strings, higher values can proceed lower values e.g. "10" comes before "7" due to "1" of course coming before "7" in the ASCII character table.

Thus, a solution similar to natsort is required with a more complex algorithm to deal with such edge cases.

Expected Result

[project]
authors = [
{ name = "Kieran Ryan" },
]
classifiers = [
...
"Programming Language :: Python :: 3 :: Only",
"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",
...
]

Actual Result

[project]
authors = [
{ name = "Kieran Ryan" },
]
classifiers = [
...
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
...
]

Support for editorconfig configuration files

Editorconfig configuration files can be placed at the root of a project to instruct tools, IDEs, etc. on the configuration to use - essentially a source of truth.

EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable and they work nicely with version control systems.

Adding .editorconfig support would enable the tool to read and respect a user's central configuration. Assessment would need to be completed to determine whether this would be an opt-in feature - perhaps by specifying to use it in the pyproject.toml OR otherwise whether to autodetect and use the settings from a present .editorconfig file.

Continuous integration pipeline does not run against external pull requests

The continuous integration pipeline and static analysis jobs only run against the source code on 'push', not on 'pull_request' (as evidenced by #20); this means linting jobs, etc. will not run against pull requests from forked repositories.

The following configuration:

on: [push]

Must change to:

on: [push, pull_request]

Command line argument to specify pyproject.toml path

The package must currently be executed in the same working directory as the pyproject.toml file in order to format it. While this file is typically placed at the root of the project, this may not always be the case. Further, it may be desired to run the application while not within the current working directory containing the configuration file. Thus it would be useful to be able to specify the file path as an optional command line argument - continuing to check the current directory when no argument is supplied:

pyprojectsort ../pyproject.toml

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.