Skip to content

Conversation

@deathaxe
Copy link
Member

@deathaxe deathaxe commented Nov 1, 2025

This commit modifies top_level_dir, which is used to resolve module names when loading unittests, to ensure, only modules from specified ST package are importet.

Without this commit, UnitTesting might attempt to import packages/modules/tests from Lib/ directory due to possible ambiguities.

Scenario:

Plugins of MyPackage are organized in Packages/MyPackage/dirname/ and tests in Packages/MyPackage/dirname/tests/. If a library Lib/pythonXX/dirname exists, Unittesting would have attempted to import tests from that directory, because test module paths would have been translated to dirname.tests due to top_level_dir pointing to Packages/MyPackage.

By adjusting top_level_dir to Packages/, test module names are translated to MyPackage.dirname.tests instead.

This commit modifies `top_level_dir`, which is used to resolve module names
when loading unittests, to ensure, only modules from specified ST package are
importet.

Without this commit, UnitTesting might attempt to import packages/modules/tests
from Lib/ directory due to possible ambiguities.

Scenario:

Plugins of `MyPackage` are organized in `Packages/MyPackage/dirname/` and tests
in `Packages/MyPackage/dirname/tests/`. If a library `Lib/pythonXX/dirname`
exists, Unittesting would have attempted to import tests from that directory,
because test module paths would have been translated to `dirname.tests` due to
`top_level_dir` pointing to `Packages/MyPackage`.

By adjusting `top_level_dir` to `Packages/`, test module names are translated
to `MyPackage.dirname.tests` instead.
@deathaxe deathaxe force-pushed the fix/nested-test-dirs branch from 88aec60 to d3f1b00 Compare November 1, 2025 10:41
@deathaxe deathaxe merged commit 330340a into master Nov 1, 2025
15 checks passed
@deathaxe deathaxe deleted the fix/nested-test-dirs branch November 1, 2025 10:43
@HorlogeSkynet
Copy link

Hello @deathaxe, FYI this commit seems to have broken really simple setups (UnitTesting documentation followed early 2025 for SSHubl).

  • Tree :
main.py
dirname/
    __init__.py
    *.py
tests/
    __init__.py
    test_*.py

The only "fix" that I've found so far is to :

  • Create a top-level __init__.py
  • Migrate test imports to : from PackageName.dirname.module import symbol
  • (Migrate unittest mock patches to PackageName.dirname.module.library.other_symbol)

WDYT ? Did we miss something in UnitTesting setup, or this is definitely an UnitTesting v1 regression ?
Thanks, bye 🙏

@deathaxe
Copy link
Member Author

The package (e.g. PackageName) is now the root package being tested, while it was a root for all global imports before. This is a major difference between ST packages and repositories hosting normal python packages. The latter ones would contain a folder dirname which is the package to test and which is available for imports import dirname in a normal runtime environment. A ST package however is represented by the repositorie's worktree itself. Any folder within is a sub-package, only, and therefore wouldn't be accessible via import in normal runtime environment without also specifying main package name.

UnitTesting not respecting this difference was a mistake (or side-effect of former ST2 environments), which could have caused ambiguites with libraries using same name as the modules from a sub-package to test. If e.g. a library dirname exists in ~/.config/sublime-text/Lib/python38 imports would fail.

Assuming, tests are always executed by UnitTesting within ST plugin environment, a top-level __init__.py in package root is not required for ST to treat it as a proper python package.

As a consequence of this PR, test modules need to use relative imports (e.g.: from ..dirname.module import symbol) to import symbols from any plugin or sub-package. That's important as ST packages may contain whitespace, which is not supported by normal import statements.

Functions like patch() need to be provided with the package name patch("PackageName.dirname.module")` as long as they don't also support relative module names.

HorlogeSkynet added a commit to HorlogeSkynet/SSHubl that referenced this pull request Nov 15, 2025
@HorlogeSkynet
Copy link

Thanks for your detailed answer.

Whereas it may actually fix some UnitTesting usages as #67, it's definitely a regression/breaking change for others.
Also, releasing it as a "patch" version (in SemVer terms) prevents one to Action version to a working UnitTesting major/minor release (e.g. SublimeText/UnitTesting/actions/run-tests@v1.8). Developers will need to explicitly freeze to v1.8.3 or apply required changes (w/o the possibility of reverting to a previous Action version, due to both forward and backward incompatibilities).

I've fixed our downstream very case, and as a side-note, a top __init__.py is needed for Mypy to correctly consider PackageName/ a Python package, before going down to PackageName/packagename (when we use the ..packagename syntax)...

Bye 👋

deathaxe added a commit that referenced this pull request Nov 15, 2025
This commit clarifies how test cases are expected to import or address
modules of a Sublime Text package as it slightly changed in PR #277.
@deathaxe
Copy link
Member Author

deathaxe commented Nov 15, 2025

This patch was tested against various popular ST packages (e.g.: GitSavvy, LSP, MarkdownEditing, NeoVintagious, etc.), none of which breaks. Admittedly all of them respect ST's Package/plugin infrastructure and therefore use absolute imports including package names. Hence it specifically just fixed an ambiguity with testing Package Control, which provides a package_control library and uses same name for a sub-directory.

Hence impact was considdered rather low. As a "breaking" change, it would have required major version to be bumbed, which however is not justified by just such a little change.

Especially, as the required import and folder structure is not documented or specified anywhere. There are many packages which use tests correct. Those which don't respect ST3's package or plugin structure, may need adjustments.

Edit: mypy needing a top-level __init__.py is a mypy issue, unrelated with UnitTesting. Other type checkers leveraged via LSP, such as pyright, seem to be fine without it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants