Skip to content

Commit db533f6

Browse files
committed
general: migrate to src/ package structure, update CI configs
1 parent 0476e8d commit db533f6

33 files changed

+124
-42
lines changed

.ci/release

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ import shutil
2121

2222
is_ci = os.environ.get('CI') is not None
2323

24-
def main():
24+
def main() -> None:
2525
import argparse
2626
p = argparse.ArgumentParser()
2727
p.add_argument('--test', action='store_true', help='use test pypi')
2828
args = p.parse_args()
2929

3030
extra = []
3131
if args.test:
32-
extra.extend(['--repository-url', 'https://test.pypi.org/legacy/'])
32+
extra.extend(['--repository', 'testpypi'])
3333

3434
root = Path(__file__).absolute().parent.parent
3535
os.chdir(root) # just in case

.ci/run

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
1-
#!/bin/bash -eu
1+
#!/bin/bash
2+
set -eu
23

34
cd "$(dirname "$0")"
4-
cd ..
5+
cd .. # git root
56

67
if ! command -v sudo; then
7-
# CI or Docker sometimes don't have it, so useful to have a dummy
8+
# CI or Docker sometimes doesn't have it, so useful to have a dummy
89
function sudo {
910
"$@"
1011
}
1112
fi
1213

1314
if [ -n "${CI-}" ]; then
1415
# install OS specific stuff here
15-
if [[ "$OSTYPE" == "darwin"* ]]; then
16+
case "$OSTYPE" in
17+
darwin*)
1618
# macos
1719
:
18-
else
20+
;;
21+
cygwin* | msys* | win*)
22+
# windows
1923
:
20-
fi
24+
;;
25+
*)
26+
# must be linux?
27+
:
28+
;;
29+
esac
30+
fi
31+
32+
33+
PY_BIN="python3"
34+
# some systems might have python pointing to python3
35+
if ! command -v python3 &> /dev/null; then
36+
PY_BIN="python"
2137
fi
2238

23-
pip3 install --user tox
24-
tox
39+
"$PY_BIN" -m pip install --user tox
40+
"$PY_BIN" -m tox --parallel --parallel-live "$@"

.github/workflows/main.yml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,32 @@ on:
1010
pull_request: # needed to trigger on others' PRs
1111
# Note that people who fork it need to go to "Actions" tab on their fork and click "I understand my workflows, go ahead and enable them".
1212
workflow_dispatch: # needed to trigger workflows manually
13-
# todo cron?
13+
# todo cron?
14+
inputs:
15+
debug_enabled:
16+
type: boolean
17+
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
18+
required: false
19+
default: false
1420

15-
env:
16-
# useful for scripts & sometimes tests to know
17-
CI: true
1821

1922
jobs:
2023
build:
2124
strategy:
2225
fail-fast: false
2326
matrix:
2427
platform: [ubuntu-latest, macos-latest, windows-latest]
25-
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
28+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12.0-rc.3']
29+
# vvv just an example of excluding stuff from matrix
30+
# exclude: [{platform: macos-latest, python-version: '3.6'}]
2631

2732
runs-on: ${{ matrix.platform }}
2833

2934
steps:
3035
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
3136
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
32-
3337
- if: ${{ matrix.platform == 'macos-latest' && matrix.python-version == '3.11' }}
38+
# hmm somehow only seems necessary for 3.11 on osx??
3439
run: echo "$HOME/Library/Python/${{ matrix.python-version }}/bin" >> $GITHUB_PATH
3540

3641
- uses: actions/setup-python@v4
@@ -41,12 +46,14 @@ jobs:
4146
with:
4247
submodules: recursive
4348

44-
# uncomment for SSH debugging
45-
# - uses: mxschmitt/action-tmate@v3
49+
- uses: mxschmitt/action-tmate@v3
50+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
4651

47-
- run: .ci/run
52+
# explicit bash command is necessary for Windows CI runner, otherwise it thinks it's cmd...
53+
- run: bash .ci/run
4854

49-
- uses: actions/upload-artifact@v3
55+
- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
56+
uses: actions/upload-artifact@v3
5057
with:
5158
name: .coverage.mypy_${{ matrix.platform }}_${{ matrix.python-version }}
5259
path: .coverage.mypy/
@@ -62,7 +69,7 @@ jobs:
6269

6370
- uses: actions/setup-python@v4
6471
with:
65-
python-version: '3.7'
72+
python-version: '3.8'
6673

6774
- uses: actions/checkout@v3
6875
with:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ doc: cog
1111
## Update files using cog.py
1212
cog: orgparse/__init__.py
1313
orgparse/__init__.py: README.rst
14-
cd orgparse && cog.py -r __init__.py
14+
cd src/orgparse && cog.py -r __init__.py

conftest.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# this is a hack to monkey patch pytest so it handles tests inside namespace packages without __init__.py properly
2+
# without it, pytest can't discover the package root for some reason
3+
# also see https://github.com/karlicoss/pytest_namespace_pkgs for more
4+
5+
import pathlib
6+
from typing import Optional
7+
8+
import _pytest.main
9+
import _pytest.pathlib
10+
11+
# we consider all dirs in repo/ to be namespace packages
12+
root_dir = pathlib.Path(__file__).absolute().parent.resolve() / 'src'
13+
assert root_dir.exists(), root_dir
14+
15+
# TODO assert it contains package name?? maybe get it via setuptools..
16+
17+
namespace_pkg_dirs = [str(d) for d in root_dir.iterdir() if d.is_dir()]
18+
19+
# resolve_package_path is called from _pytest.pathlib.import_path
20+
# takes a full abs path to the test file and needs to return the path to the 'root' package on the filesystem
21+
resolve_pkg_path_orig = _pytest.pathlib.resolve_package_path
22+
def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
23+
result = path # search from the test file upwards
24+
for parent in result.parents:
25+
if str(parent) in namespace_pkg_dirs:
26+
return parent
27+
raise RuntimeError("Couldn't determine path for ", path)
28+
_pytest.pathlib.resolve_package_path = resolve_package_path
29+
30+
31+
# without patching, the orig function returns just a package name for some reason
32+
# (I think it's used as a sort of fallback)
33+
# so we need to point it at the absolute path properly
34+
# not sure what are the consequences.. maybe it wouldn't be able to run against installed packages? not sure..
35+
search_pypath_orig = _pytest.main.search_pypath
36+
def search_pypath(module_name: str) -> str:
37+
return str(root_dir)
38+
_pytest.main.search_pypath = search_pypath

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ addopts =
88

99
# otherwise it won't discover doctests
1010
--doctest-modules
11+
12+
# show all test durations (unless they are too short)
13+
--durations=0

setup.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55

66
def main():
7-
pkg = 'orgparse'
8-
subpkgs = find_namespace_packages('.', include=(pkg + '.*',))
7+
pkgs = find_namespace_packages('src')
8+
pkg = min(pkgs)
99

10-
import orgparse
1110
setup(
1211
name=pkg,
1312
use_scm_version={
@@ -18,22 +17,24 @@ def main():
1817

1918
zip_safe=False,
2019

21-
packages=[pkg, *subpkgs],
20+
packages=pkgs,
21+
package_dir={'': 'src'},
2222
package_data={
2323
pkg: ['py.typed'], # todo need the rest as well??
2424
'orgparse.tests.data': ['*.org'],
2525
},
2626

27-
author=orgparse.__author__,
27+
author='Takafumi Arakaki, Dmitrii Gerasimov',
2828
author_email='[email protected]',
2929
maintainer='Dima Gerasimov (@karlicoss)',
3030
maintainer_email='[email protected]',
3131

3232
url='https://github.com/karlicoss/orgparse',
33-
license=orgparse.__license__,
33+
license='BSD License',
3434

3535
description='orgparse - Emacs org-mode parser in Python',
36-
long_description=orgparse.__doc__,
36+
# TODO add it back later, perhaps via ast?
37+
# long_description=orgparse.__doc__,
3738

3839
keywords='org org-mode emacs',
3940
classifiers=[

orgparse/__init__.py renamed to src/orgparse/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@
113113

114114
from .node import parse_lines, OrgEnv, OrgNode # todo basenode??
115115

116-
__author__ = 'Takafumi Arakaki, Dmitrii Gerasimov'
117-
__license__ = 'BSD License'
118116
__all__ = ["load", "loads", "loadi"]
119117

120118

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

orgparse/tests/test_data.py renamed to src/orgparse/tests/test_data.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
def load_data(path):
1414
"""Load data from python file"""
1515
ns = {} # type: ignore
16-
exec(Path(path).read_text(), ns)
16+
# read_bytes() and compile hackery to avoid encoding issues (e.g. see 05_tags)
17+
exec(compile(Path(path).read_bytes(), path, 'exec'), ns)
1718
return ns['data']
1819

1920

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

tox.ini

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,39 @@
22
minversion = 3.7
33
# relies on the correct version of Python installed
44
envlist = tests,mypy
5+
# https://github.com/tox-dev/tox/issues/20#issuecomment-247788333
6+
# hack to prevent .tox from crapping to the project directory
7+
toxworkdir={env:TOXWORKDIR_BASE:}{toxinidir}/.tox
58

69
[testenv]
7-
passenv = CI,CI_*
10+
# TODO how to get package name from setuptools?
11+
package_name = "orgparse"
12+
passenv =
13+
# useful for tests to know they are running under ci
14+
CI
15+
CI_*
16+
# respect user's cache dirs to prevent tox from crapping into project dir
17+
MYPY_CACHE_DIR
18+
PYTHONPYCACHEPREFIX
819

20+
21+
# note: --use-pep517 here is necessary for tox --parallel flag to work properly
22+
# otherwise it seems that it tries to modify .eggs dir in parallel and it fails
923
[testenv:tests]
1024
commands =
11-
pip install -e .[testing]
12-
python -m pytest --ignore-glob='**/_py3compat.py' orgparse {posargs}
25+
{envpython} -m pip install --use-pep517 -e .[testing]
26+
# posargs allow test filtering, e.g. tox ... -- -k test_name
27+
{envpython} -m pytest \
28+
--pyargs {[testenv]package_name} \
29+
{posargs}
30+
1331

1432
[testenv:mypy]
1533
commands =
16-
pip install -e .[linting]
17-
python -m mypy --install-types --non-interactive \
18-
orgparse \
19-
# txt report is a bit more convenient to view on CI
20-
--txt-report .coverage.mypy \
21-
--html-report .coverage.mypy \
22-
{posargs}
34+
{envpython} -m pip install --use-pep517 -e .[linting]
35+
{envpython} -m mypy --install-types --non-interactive \
36+
-p {[testenv]package_name} \
37+
# txt report is a bit more convenient to view on CI
38+
--txt-report .coverage.mypy \
39+
--html-report .coverage.mypy \
40+
{posargs}

0 commit comments

Comments
 (0)