Make pyproject.toml PEP 621 compliant

- converted tool.poetry to PEP 621 (default behavior for poetry)
- converted setup.py to PEP 621 (recommended by setuptools)
- use pyproject.toml instead of version.txt
- use importlib instead of version.txt
- use PEP 621 dependency format in flatpak build scripts
- added tomli as a dev dependency (used in build scripts)
- removed version.txt (no longer used)
- cleaned up dependencies

- version is still in desktop/setup.py (unclear on how snapcraft uses it)
- poetry lockfiles need to be rebuilt
This commit is contained in:
Timothy Cyrus 2025-03-13 18:38:11 -04:00
parent 2cee0508d1
commit 5e89cdcbc4
12 changed files with 197 additions and 175 deletions

View file

@ -7,7 +7,6 @@ Unless you're a core OnionShare developer making a release, you'll probably neve
### Update the version in these places
- [ ] `cli/pyproject.toml`
- [ ] `cli/onionshare_cli/resources/version.txt`
- [ ] `desktop/pyproject.toml`
- [ ] `desktop/setup.py`
- [ ] `desktop/org.onionshare.OnionShare.appdata.xml`
@ -118,7 +117,7 @@ In `flatpak/org.onionshare.OnionShare.yaml`:
# get onionshare dependencies
cd flatpak-build-tools/pip
./flatpak-pip-generator $(../../onionshare/flatpak/poetry-to-requirements.py ../../onionshare/desktop/pyproject.toml | grep -v PySide6)
./flatpak-pip-generator $(../../onionshare/flatpak/pyproject-to-requirements.py ../../onionshare/desktop/pyproject.toml | grep -v PySide6)
../flatpak-json2yaml.py ./python3-modules.json
mv python3-modules.yml onionshare-desktop.yaml
```
@ -285,7 +284,7 @@ Now, notarize the release. You will need an app-specific Apple ID password set u
```sh
export APPLE_PASSWORD="changeme" # This must be an app-specific Apple ID password, not your main Apple ID password
export VERSION=$(cat ../cli/onionshare_cli/resources/version.txt)
export VERSION=$(cat ../cli/pyproject.toml | python -c 'import tomllib,sys;print(tomllib.load(sys.stdin)["project"]["version"])')
# Notarize it
xcrun notarytool submit --apple-id "you@example.com" --team-id 7WLJ4UBL5L --password "$APPLE_PASSWORD" --progress --wait dist/OnionShare-$VERSION.dmg

View file

@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import base64
import hashlib
import importlib.metadata as importlib_metadata
import importlib.resources as importlib_resources
import os
import platform
@ -59,8 +60,7 @@ class Common:
self.platform = "BSD"
# The current version of OnionShare
with open(self.get_resource_path("version.txt")) as f:
self.version = f.read().strip()
self.version = importlib_metadata.version(__package__)
def display_banner(self):
"""

View file

@ -1 +0,0 @@
2.6.3

View file

@ -1,9 +1,16 @@
[tool.poetry]
[project]
name = "onionshare_cli"
version = "2.6.3"
description = "OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service."
authors = ["Micah Lee <micah@micahflee.com>"]
license = "GPLv3+"
description = "Securely and anonymously share files, host websites, and chat with friends using the Tor network"
requires-python = ">=3.10,<3.13"
authors = [
{ name = "Micah Lee", email = "micah@micahflee.com" },
]
maintainers = [
{ name = "Micah Lee", email = "micah@micahflee.com" },
]
license = { text = "GPL-3.0-or-later" }
keywords = [ "onion", "share", "onionshare", "tor", "anonymous", "web server" ]
classifiers = [
"Programming Language :: Python :: 3",
"Framework :: Flask",
@ -14,39 +21,58 @@ classifiers = [
"Operating System :: OS Independent",
"Environment :: Web Environment",
]
dependencies = [
"click",
"flask == 2.3.2",
"flask-compress >= 1.13, < 2.0",
"flask-socketio == 5.3.4",
"psutil",
"pysocks",
"requests[socks]",
"unidecode",
"urllib3 >= 2.2.2, < 3.0",
"eventlet",
"pynacl",
"colorama",
"gevent-websocket",
"stem == 1.8.1",
"waitress >= 3.0.1, < 4.0.0",
"werkzeug == 3.0.6",
"packaging >= 24",
"gevent >= 23.9.1, < 24.0.0",
"qrcode >= 7.4.2, < 8.0.0",
]
[tool.poetry.dependencies]
python = ">=3.10,<3.13"
click = "*"
flask = "2.3.2"
flask-compress = "^1.13"
flask-socketio = "5.3.4"
psutil = "*"
pysocks = "*"
requests = {extras = ["socks"], version = "*"}
unidecode = "*"
urllib3 = "^2.2.2"
eventlet = "*"
setuptools = ">=70.0.0"
pynacl = "*"
colorama = "*"
gevent-websocket = "*"
stem = "1.8.1"
waitress = "^3.0.1"
werkzeug = "3.0.6"
packaging = ">=24"
gevent = "^23.9.1"
wheel = "^0.41.2"
cffi = "^1.15.1"
cython = "^3.0.2"
qrcode = "^7.4.2"
[project.urls]
homepage = "https://onionshare.org"
repository = "https://github.com/onionshare/onionshare"
documentation = "https://docs.onionshare.org/"
[project.scripts]
onionshare-cli = "onionshare_cli:main"
[tool.setuptools]
packages = [
"onionshare_cli",
"onionshare_cli.web",
]
[tool.setuptools.package-data]
onionshare_cli = [
"resources/*",
"resources/static/*",
"resources/static/css/*",
"resources/static/img/*",
"resources/static/js/*",
"resources/templates/*",
]
[tool.poetry.group.dev.dependencies]
setuptools = ">=70.0.0"
[tool.poetry.group.test.dependencies]
pytest = ">=7.2.0"
[tool.poetry.scripts]
onionshare-cli = 'onionshare_cli:main'
[build-system]
requires = ["poetry-core"]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

View file

@ -18,55 +18,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import setuptools
from setuptools import setup
with open(os.path.join("onionshare_cli", "resources", "version.txt")) as f:
version = f.read().strip()
setuptools.setup(
name="onionshare-cli",
version=version,
long_description=(
"OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, "
"making it accessible as a Tor onion service, and generating an unguessable web address so others can "
"download files from you, or upload files to you. It does _not_ require setting up a separate server or "
"using a third party file-sharing service."
),
author="Micah Lee",
author_email="micah@micahflee.com",
maintainer="Micah Lee",
maintainer_email="micah@micahflee.com",
url="https://onionshare.org",
license="GPLv3",
keywords="onion, share, onionshare, tor, anonymous, web server",
classifiers=[
"Programming Language :: Python :: 3",
"Framework :: Flask",
"Topic :: Communications :: File Sharing",
"Topic :: Security :: Cryptography",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Intended Audience :: End Users/Desktop",
"Operating System :: OS Independent",
"Environment :: Web Environment",
],
packages=[
"onionshare_cli",
"onionshare_cli.web",
],
package_data={
"onionshare_cli": [
"resources/*",
"resources/static/*",
"resources/static/css/*",
"resources/static/img/*",
"resources/static/js/*",
"resources/templates/*",
]
},
entry_points={
"console_scripts": [
"onionshare-cli = onionshare_cli:main",
],
},
)
setup()

View file

@ -1,31 +1,78 @@
[tool.poetry]
[project]
name = "onionshare"
version = "2.6.3"
description = "OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service."
authors = ["Micah Lee <micah@micahflee.com>"]
license = "GPLv3+"
requires-python = ">=3.10,<3.13"
description = "Securely and anonymously share files, host websites, and chat with friends using the Tor network"
authors = [
{ name = "Micah Lee", email = "micah@micahflee.com" },
]
maintainers = [
{ name = "Micah Lee", email = "micah@micahflee.com" },
]
license = { text = "GPL-3.0-or-later" }
keywords = [ "onion", "share", "onionshare", "tor", "anonymous", "web server" ]
classifiers = [
"Programming Language :: Python :: 3",
"Framework :: Flask",
"Topic :: Communications :: File Sharing",
"Topic :: Security :: Cryptography",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Intended Audience :: End Users/Desktop",
"Operating System :: OS Independent",
"Environment :: Web Environment",
]
dependencies = [
"PySide6 == 6.8.2.1",
"qrcode",
"werkzeug == 3.0.6",
"python-gnupg",
"onionshare_cli",
]
[project.urls]
homepage = "https://onionshare.org"
repository = "https://github.com/onionshare/onionshare"
documentation = "https://docs.onionshare.org/"
[project.scripts]
onionshare = 'onionshare:main'
onionshare-cli = "onionshare_cli:main"
[tool.setuptools]
packages = [
"onionshare",
"onionshare.tab",
"onionshare.tab.mode",
"onionshare.tab.mode.share_mode",
"onionshare.tab.mode.receive_mode",
"onionshare.tab.mode.website_mode",
"onionshare.tab.mode.chat_mode",
]
[tool.setuptools.package-data]
onionshare = [
"resources/*",
"resources/images/*",
"resources/images/countries/*",
"resources/locale/*",
"resources/countries/*",
]
[tool.poetry.dependencies]
python = ">=3.10,<3.13"
onionshare_cli = {path = "../cli", develop = true}
PySide6 = "6.8.2.1"
qrcode = "*"
werkzeug = "3.0.6"
python-gnupg = "*"
onionshare_cli = { path = "../cli", develop = true }
[tool.poetry.group.dev.dependencies]
click = "*"
black = "*"
cx_freeze = "7.2.9"
importlib-metadata = "*"
tomli = { version = "^2.0.1", python = "<3.11" }
[tool.poetry.group.test.dependencies]
pytest = "*"
pytest-faulthandler = "*"
pytest-qt = "*"
cx_freeze = "7.2.9"
importlib-metadata = "*"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
onionshare = 'onionshare:main'
onionshare-cli = 'onionshare_cli:main'

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import os
import sys
import inspect
import click
import platform
@ -8,6 +9,11 @@ import shutil
import glob
import itertools
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
from common import get_binary_arches
root = os.path.dirname(
@ -270,9 +276,10 @@ def package(app_path):
return
print("> Create DMG")
version_filename = f"{root}/cli/onionshare_cli/resources/version.txt"
with open(version_filename) as f:
version = f.read().strip()
pyproject_filename = f"{root}/cli/pyproject.toml"
with open(pyproject_filename, "rb") as f:
pyproject = tomllib.load(f)
version = pyproject['project']['version']
os.makedirs(f"{desktop_dir}/dist", exist_ok=True)
dmg_path = f"{desktop_dir}/dist/OnionShare-{version}.dmg"

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python3
from setuptools.command.build import build
import sys
import os
import sys
import inspect
import click
import shutil
@ -10,6 +9,11 @@ import uuid
import xml.etree.ElementTree as ET
from glob import glob
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
root = os.path.dirname(
os.path.dirname(
os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
@ -165,13 +169,9 @@ def wix_build_components_xml(root, data):
return component_ids
def msi_package(build_path, msi_path, product_update_code):
def msi_package(build_path, msi_path, product_update_code, version):
print(f"> Build the WiX file")
version_filename = os.path.join(
build_path, "lib", "onionshare_cli", "resources", "version.txt"
)
with open(version_filename) as f:
version = f.read().strip()
# change a version like 2.6.dev1 to just 2.6, for cx_Freeze's sake
last_digit = version[-1]
if version.endswith(f".dev{last_digit}"):
@ -428,16 +428,18 @@ def codesign(path):
@click.argument("path")
def package(path):
"""Build the MSI package"""
version_filename = os.path.join(
root, "cli", "onionshare_cli", "resources", "version.txt"
pyproject_filename = os.path.join(
root, "cli", "pyproject.toml"
)
with open(version_filename) as f:
version = f.read().strip()
with open(pyproject_filename, "rb") as f:
pyproject = tomllib.load(f)
version = pyproject['project']['version']
msi_package(
path,
os.path.join(desktop_dir, "dist", f"OnionShare-win64-{version}.msi"),
"ed7f9243-3528-4b4a-b85c-9943982e75eb",
version
)

View file

@ -26,9 +26,16 @@ import cx_Freeze
from cx_Freeze import setup, Executable
from setuptools import find_packages
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
# Discover the version
with open(os.path.join("..", "cli", "onionshare_cli", "resources", "version.txt")) as f:
version = f.read().strip()
pyproject_filename = os.path.join("..", "cli", "pyproject.toml")
with open(pyproject_filename, "rb") as f:
pyproject = tomllib.load(f)
version = pyproject['project']['version']
# change a version like 2.6.dev1 to just 2.6, for cx_Freeze's sake
last_digit = version[-1]
if version.endswith(f".dev{last_digit}"):

View file

@ -1,53 +1,11 @@
#!/usr/bin/env python3
# This file is used to build the Snapcraft and Flatpak packages
import setuptools
from setuptools import setup
# The version must be hard-coded because Snapcraft won't have access to ../cli
version = "2.6.3"
setuptools.setup(
setup(
name="onionshare",
version=version,
long_description="Securely and anonymously share files, host websites, and chat with friends using the Tor network",
author="Micah Lee",
author_email="micah@micahflee.com",
maintainer="Micah Lee",
maintainer_email="micah@micahflee.com",
url="https://onionshare.org",
license="GPLv3",
keywords="onion, share, onionshare, tor, anonymous, web server",
classifiers=[
"Programming Language :: Python :: 3",
"Framework :: Flask",
"Topic :: Communications :: File Sharing",
"Topic :: Security :: Cryptography",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Intended Audience :: End Users/Desktop",
"Operating System :: OS Independent",
"Environment :: Web Environment",
],
packages=[
"onionshare",
"onionshare.tab",
"onionshare.tab.mode",
"onionshare.tab.mode.share_mode",
"onionshare.tab.mode.receive_mode",
"onionshare.tab.mode.website_mode",
"onionshare.tab.mode.chat_mode",
],
package_data={
"onionshare": [
"resources/*",
"resources/images/*",
"resources/images/countries/*",
"resources/locale/*",
"resources/countries/*",
]
},
entry_points={
"console_scripts": [
"onionshare = onionshare:main",
"onionshare-cli = onionshare_cli:main",
],
},
)

View file

@ -1,6 +1,6 @@
#!/bin/bash
VERSION=$(cat ../cli/onionshare_cli/resources/version.txt)
VERSION=$(cat ../cli/pyproject.toml | python -c 'import tomllib,sys;print(tomllib.load(sys.stdin)["project"]["version"])')
# Supported locales
LOCALES="en sq bg zh_CN de el ga ja pl ru es tr uk"

View file

@ -1,7 +1,12 @@
#!/usr/bin/env python3
import toml
import sys
import click
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
def format_version(dep, version):
if version == "*":
@ -30,7 +35,7 @@ def format_version(dep, version):
def poetry_to_requirements(pyproject_filename):
"""Convert poetry dependencies in a pyproject.toml to requirements format."""
with open(pyproject_filename, "r") as f:
data = toml.load(f)
data = tomllib.load(f)
dependencies = data.get("tool", {}).get("poetry", {}).get("dependencies", {})
@ -48,5 +53,26 @@ def poetry_to_requirements(pyproject_filename):
print(req)
@click.command()
@click.argument("pyproject_filename")
def pyproject_to_requirements(pyproject_filename):
"""Convert PEP 631 dependencies in a pyproject.toml to requirements format."""
with open(pyproject_filename, "r") as f:
data = tomllib.load(f)
dependencies = data.get("project", {}).get("dependencies", [])
requirements = []
for dep in dependencies:
if dep == "onionshare_cli":
continue
requirements.append(dependencies)
for req in requirements:
print(req)
if __name__ == "__main__":
poetry_to_requirements()
pyproject_to_requirements()