Skip to content

Commit

Permalink
general: switch from flat layout to src layout
Browse files Browse the repository at this point in the history
this is long overdue and much friendlier to python tooling ecosystem

should be backwards compatible with existing editable installs, with a warning to reinstall properly

see #316
  • Loading branch information
karlicoss committed Jan 24, 2025
1 parent bb703c8 commit 6d9913a
Show file tree
Hide file tree
Showing 206 changed files with 79 additions and 23 deletions.
5 changes: 4 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import _pytest.pathlib

# we consider all dirs in repo/ to be namespace packages
root_dir = pathlib.Path(__file__).absolute().parent.resolve() # / 'src'
root_dir = pathlib.Path(__file__).absolute().parent.resolve() / 'src'
assert root_dir.exists(), root_dir

# TODO assert it contains package name?? maybe get it via setuptools..
Expand All @@ -21,6 +21,9 @@
# takes a full abs path to the test file and needs to return the path to the 'root' package on the filesystem
resolve_pkg_path_orig = _pytest.pathlib.resolve_package_path
def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
if 'tests/conftest.py' in str(path):
return resolve_pkg_path_orig(path)

result = path # search from the test file upwards
for parent in result.parents:
if str(parent) in namespace_pkg_dirs:
Expand Down
1 change: 1 addition & 0 deletions my
9 changes: 0 additions & 9 deletions my/core/internal.py

This file was deleted.

8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@


def main() -> None:
pkg = 'my'
subpackages = find_namespace_packages('.', include=('my.*',))
pkgs = find_namespace_packages('src')
pkg = min(pkgs) # should be just 'my'

setup(
name='HPI', # NOTE: 'my' is taken for PyPi already, and makes discovering the project impossible. so we're using HPI
use_scm_version={
Expand All @@ -30,7 +31,8 @@ def main() -> None:

# eh. find_packages doesn't find anything
# find_namespace_packages can't find single file packages (like my/common.py)
packages=[pkg, *subpackages],
packages=pkgs,
package_dir={'': 'src'},
package_data={
pkg: [
# for mypy
Expand Down
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.
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
21 changes: 13 additions & 8 deletions my/core/__init__.py β†’ src/my/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@
LazyLogger = make_logger # TODO deprecate this in favor of make_logger


if not TYPE_CHECKING:
# we used to keep these here for brevity, but feels like it only adds confusion,
# e.g. suggest that we perhaps somehow modify builtin behaviour or whatever
# so best to prefer explicit behaviour
from dataclasses import dataclass
from pathlib import Path


__all__ = [
'__NOT_HPI_MODULE__',
'Json',
Expand Down Expand Up @@ -59,3 +51,16 @@
except:
pass
##


if not TYPE_CHECKING:
# we used to keep these here for brevity, but feels like it only adds confusion,
# e.g. suggest that we perhaps somehow modify builtin behaviour or whatever
# so best to prefer explicit behaviour
from dataclasses import dataclass
from pathlib import Path


from .internal import warn_if_not_using_src_layout

warn_if_not_using_src_layout(path=__path__)
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.
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.
54 changes: 54 additions & 0 deletions src/my/core/internal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
Utils specific to hpi core, shouldn't really be used by HPI modules
"""
from __future__ import annotations


def assert_subpackage(name: str) -> None:
# can lead to some unexpected issues if you 'import cachew' which being in my/core directory.. so let's protect against it
# NOTE: if we use overlay, name can be smth like my.origg.my.core.cachew ...
assert name == '__main__' or 'my.core' in name, f'Expected module __name__ ({name}) to be __main__ or start with my.core'


def _is_editable(package_name: str) -> bool:
import importlib.metadata

dist = importlib.metadata.distribution(package_name)
dist_files = dist.files or []
for path in dist_files:
if str(path).endswith('.pth'):
return True
return False


def warn_if_not_using_src_layout(path: list[str]) -> None:
contains_src = any('/src/my/' in p for p in path)
if contains_src:
return

# __package__ won't work because it's gonna be 'my' rather than 'hpi'
# seems like it's quite annoying to get distribition name from within the package, so easier to hardcode..
distribution_name = 'hpi'
try:
editable = _is_editable(distribution_name)
except:
# it would be annoying if this somehow fails during the very first HPI import...
# so just make defensive
return

if not editable:
# nothing to check
return


from . import warnings

MSG = '''
Seems that your HPI is installed as editable and uses flat layout ( my/ directory next to .git folder).
This was the case in older HPI versions (pre-20250123), but now src-layout is recommended ( src/my/ directory next to .git folder).
Reinstall your HPI as editable again via 'pip install --editable /path/to/hpi'.
See https://github.com/karlicoss/HPI/issues/316 for more info.
'''

warnings.high(MSG)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ uv_seed = true # seems necessary so uv creates separate venvs per tox env?
deps =
-e .[testing]
commands =
{envpython} -m ruff check my/
{envpython} -m ruff check src/


# just the very core tests with minimal dependencies
Expand All @@ -46,7 +46,7 @@ commands =
# ignore orgmode because it imports orgparse
# tbh not sure if it even belongs to core, maybe move somewhere else..
# same with pandas?
--ignore my/core/orgmode.py \
--ignore src/my/core/orgmode.py \
{posargs}


Expand Down

0 comments on commit 6d9913a

Please sign in to comment.