duckinator / bork

A Python build and release management tool.
https://bork.readthedocs.io
MIT License
12 stars 2 forks source link

Feature Request: New Project generation #24

Open AstraLuma opened 5 years ago

AstraLuma commented 5 years ago

I've lost track myself. Can you add a command to generate template pyproject.toml/setup.{cfg,py} files?

(I include setup.py because pips that don't support setup.py-less packages are still super common)

duckinator commented 5 years ago

I'm okay with this feature being added, but I'm not sure what exactly I want it to generate.

What you suggest is a good starting point, but I'm unsure exactly how much I want to go beyond that (if any).

Ruby's bundle gem is pretty slick and may be worth looking to for inspiration. I suspect there's an npm/yarn/ds-equivalent as well.

AstraLuma commented 5 years ago

Just, general purpose, low-opinion stuff? I'm thinking it would generate three files:

If you're feeling particularly opinionated, maybe include hooks for setuptools-scm?

duckinator commented 5 years ago

Yeah, I guess setup.py + setup.cfg + pyproject.toml is probably it.

wrt setuptools-scm, the main problem I see there is that setup.py stops being just bootstrap code, so it can't be freely removed.

Setting aside getting scm-related info for dev builds and the like, I'm fond of using attr: in setup.cfg atm — it has to be manually updated, but at least it's only updated in one place:

[metadata]
name = emanate
version = attr: emanate.__version__
author = attr: emanate.__author__
author_email = me@duckie.co
duckinator commented 5 years ago

I'm kinda tossing around the idea of, basically, having bork init be for making an existing project have minimal support for being managed by bork (those 3 files) and bork new for generating a whole new thing. Or something like that.

I'm not entirely sold on it, though. This is basically what the latter would look like:

$ ls -a
. ..
$ bork new some_project
$ tree some_project
some_project
├── some_project
│   └── __init__.py
├── tests
│   └── .gitkeep
├── CODE_OF_CONDUCT.txt
├── LICENSE.txt
├── README.md
├── pyproject.toml
├── setup.cfg
└── setup.py

@astronouth7303 @nbraud would either of you use functionality like this? & if so, do you think bork is the right place for it, or should the more complicated approach be a standalone tool that is designed to work alongside bork?

duckinator commented 5 years ago

The more I think about it, the more I'm thinking I do want the more complex functionality, but not in bork itself. So maybe I can just have bork init (or similar) create those 3 files in the cwd, and have a separate more full-featured thing for new projects?

AstraLuma commented 5 years ago

Yeah, you probably want something like cookie cutter for more complete project generation.

duckinator commented 5 years ago

Okay, so I'm thinking basically bork init MODULE_NAME would create something along the lines of the following files/contents. If something is set like "blah = TODO", it's because I'm not sure what to put there or even if I want to include it.

pyproject.toml:

[build-system]
# Specify the required build system.
# The version requirement is necessary to allow setup.py-less builds;
# See: https://github.com/pypa/setuptools/pull/1675
requires = ["setuptools >= 40.9.0"]
build-backend = "setuptools.build_meta"

setup.cfg:

[metadata]
name = MODULE_NAME
version = attr: MODULE_NAME.__version__
author = attr: MODULE_NAME.__author__
author_email = TODO
description = TODO
url = TODO
license = TODO
# classifiers =

[options.entry_points]
# console_scripts =

[bork]
# If you want to generate a ZipApp, you need to define zipapp_main.
# If you don't, leave it commented out.
#
# It will likely be the same as one of the values under [options.entry_points].
# zipapp_main = 

setup.py (possibly requiring a --legacy flag or something? or possibly invert that behavior, so --no-legacy doesn't generate it? idk.):

#!/usr/bin/env python3

import setuptools

setuptools.setup()
duckinator commented 5 years ago

Also, to clarify, it would plop it in the CWD. The purpose of providing MODULE_NAME is purely to fill in a few values.

AstraLuma commented 5 years ago

I figured you'd just drop in the metadata from the sample and let the developer fix it up. It doesn't have to be right, just has to be enough to get rolling.

I mean, I've been doing python for over a decade and I still don't remember all the metadata fields

AstraLuma commented 5 years ago

I'm still going to strongly encourage the use of setuptools-scm because it dramatically simplifies the use of CI/CD to produce deployment artifacts.

The amount that needs to be in setup.py is pretty minimal though? You can't go setup.py-less (yet), but https://github.com/gqlmod/gqlmod/blob/master/setup.py

duckinator commented 4 years ago

I came up with a rough idea for this. What're your thoughts, @astronouth7303 @nbraud?


Assuming bork init PACKAGE_NAME,

pyproject.toml:

[build-system]
# Specify the required build system.
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[tool.bork.aliases]
# Run aliases using `bork run <alias_name>`

# Format:
#   ALIAS_NAME = "COMMAND"

# For example, if you use pytest+pylint+flake8, you could do:
#   test = "pytest --pylint --flake8 --verbose"
# ... and then run `bork run test`

setup.cfg:

[metadata]
name = PACKAGE_NAME
# You need to define `__version__ = "<VERSION NUMBER>" in PACKAGE_NAME/__init__.py for this to work.
version = attr: PACKAGE_NAME.__version__
author = TODO
author_email = TODO
description = TODO
long_description = TODO
url = TODO
license = TODO
classifiers =
    Programming Language :: Python :: 3

[options]
include_package_data = False
packages = find:
python_requires = >=3.5

install_requires =

# If you have a setup.py, this lets `./setup.py test` install what it needs.
tests_require =
    PACKAGE_NAME[linting,testing]

[options.extras_require]
# To install both linting- and testing-related dependencies, do `pip install .[linting,testing]`

# linting-related dependencies.
# Install using `pip install .[linting]`
linting =

# testing-related dependencies.
# Install using `pip install .[testing]
testing =

[options.entry_points]
#console_scripts =

if --legacy (or something like that) is provided, setup.py:

#!/usr/bin/env python3

from setuptools import setup
setup()
AstraLuma commented 4 years ago

Can you include links to docs in the setup.cfg? In particular, the license and classifier lists.

I'm also not seeing any "long description" options.

duckinator commented 4 years ago

Yup, I can definitely do that! Will need to find them at some point (not hard; just don't have the time atm). I've also been considering making a small website for bork which includes a page with details about common setup.cfg setups.

Also, edited my last comment to include long_description, since I'll likely be copy/pasting that for the basis of this feature whenever it's implemented.

AstraLuma commented 4 years ago

long_description is usually file: README.* these days (autodetect readme, and include the appropriate MIME if need be?)

duckinator commented 4 years ago

So, as an update: I'm still okay with this, but I think a lot of refactoring needs to happen first. The codebase has gotten too unwieldy for me to feel comfortable adding entire new commands right now.

duckinator commented 1 year ago

Everything can be done in pyproject.toml now, even when using setuptools. So this means only a single file needs to be created.

pyproject.toml docs: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/

setuptools-specific docs: https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html

editable mode, no setup.py needed: https://setuptools.pypa.io/en/latest/userguide/development_mode.html (pip install -e .)

duckinator commented 1 year ago
Potential starting point for the generated `pyproject.toml` ```toml # This file was originally generated by Bork. # Please go through the file and make sure it's # configured appropriately for your project. <3 # # In particular, be sure to replace: # - PACKAGE with your package name (it is recommended to have it be all-lowercase) # - AUTHORNAME with the name of the first author # - AUTHOREMAIL with the email for the first author # - DESCRIPTION with a short description # - LICENSE with your license of choice [build-system] # Specify the required build system. requires = ["setuptools >= 61", "wheel"] build-backend = "setuptools.build_meta" [project] name = "PACKAGE" # Specify that the package version is defined dynamically. dynamic = ["version"] # List of authors of the project. authors = [ {name = "AUTHORNAME", email = "AUTHOREMAIL"}, ] # A brief description of the project. description = "DESCRIPTION" # Update this if your README file is different (e.g. README.rst). readme = "README.md" # Update this to specify the license you're using! license = {text = "LICENSE"} # None of these are *required*. # See https://pypi.org/classifiers/ for a complete list and # instructions on how to prevent uploading to PyPi using classifiers. classifiers = [ "Programming Language :: Python :: 3", ] # The runtime dependencies for your project. dependencies = [ ] # The minimum Python version you support. # You can determine which Python versions are supported at: # https://devguide.python.org/versions/ requires-python = ">= 3.8" [project.urls] # The URL to your repository. This might be on GitHub or GitLab, for example. #repository = "https://..." # The URL to your documentation. #documentation = "https://..." [project.optional-dependencies] # While your optional-dependencies can be named (basically) whatever you want, # Bork went ahead and set up some common ones for you. # You don't have to use them if you don't want to. <3 # These can be installed via `pip install .[lint]` lint = [ "pylint", ] # These can be installed via `pip install .[test]` test = [ "pytest", ] # These can be installed via `pip install .[dev]` dev = [ ".[lint]", ".[test]", "bork", ] [project.scripts] # These are the command-line scripts for your project. # If you want `some-tool` to call `main()` from `some_tool.cli`, you'd do this: # some-tool = "some_tool.cli:main" [tools.setuptools] # If auto-detection of project names doesn't work, # or you just don't want to use it for some reason, # set tools.setuptools.packages below. #packages = ["PACKAGE"] [tool.setuptools.dynamic] # This assumes your project exports `PACKAGE.__version__`. # If you define it elsewhere, update this! version = {attr = "PACKAGE.__version__"} [tool.bork.zipapp] # If true, generate a Zipapp. Otherwise, don't. enabled = false # The entrypoint for the zipapp. This is usually the same # as one of the values in [project.scripts], but sometimes # you need zipapp-specific workarounds. main = "some_module.cli:main" [tool.bork.release] # If true, release to PyPi. pypi = false # If true, release to GitHub. github = false # The GitHub repository to upload releases to. #github_repository = "some-user/some-repo" # If true, remove "-" from the zipapp name when # uploading to e.g. GitHub. strip_zipapp_version = true [tool.bork.aliases] #lint = [ # "pylint PACKAGE", #] # Runs all tests. test = "pytest --verbose" ```