Skip to content

Comments

Restructure the project & public API clarification from code#1103

Merged
ItsDrike merged 3 commits intomasterfrom
restructure-project
Feb 14, 2026
Merged

Restructure the project & public API clarification from code#1103
ItsDrike merged 3 commits intomasterfrom
restructure-project

Conversation

@ItsDrike
Copy link
Member

@ItsDrike ItsDrike commented Feb 8, 2026

This PR performs a major restructure of the mcstatus codebase with the goal of making API boundaries explicit and producing an overall less cluttered, more structured code tree.

High-level changes overview

  • Internal implementation details are now consistently following proper naming, with private internals being under _ prefixed namespaces (package, module, or object names), instead of living alongside public modules.
  • The old monolithic responses.py file has been split into a proper package with shared base classes, per-response type modules and a separate _raw module for TypedDict definitions.
  • The communication internals (pingers, querier, bedrock/legacy status classes) were moved under the protocol package, and renamed to following the same consistent and more obvious naming scheme:
    • ServerPinger -> JavaClient
    • LegacyServerStatus -> LegacyClient
    • ServerQuerier -> QueryClient
    • BedrockServerStatus -> BedrockClient

Compatibility handling

Deprecated public modules are no longer present directly in the source tree. Instead, they live under an explicit compatibility shims package (mcstatus/_compat) and are injected into the sdist at build time via Hatchling force-include setting.

This preserves deprecated import paths accessible for end users of the library while preventing internal code from accidentally depending on them, keeping these shims from cluttering the new source tree.

Additionally, Ruff configuration was added to explicitly forbid importing from _compat internally, helping prevent accidental usage (like via LSP auto-imports).

Warning

If for some reason, someone relies on using mcstatus as a direct git submodule, this deprecation behavior will not function for them. I don't think we need to consider this as an issue, but I wanted to mention it.

This also moves the already deprecated mcstatus/status_responses.py under _compat and explicitly deprecates mcstatus/motd/transformers.py on a module import level, not just with the existing the class usage deprecation messages.

Breaking changes / Explicit public API boundaries

This PR attempts to establish a new code-style guideline for mcstatus where private internals live under _ prefixed packages or modules.

If a package itself is _ prefixed, modules inside it are considered private by default and generally do not need to be _ prefixed themselves. If a _ prefixed module exists inside a _ prefixed package, it should be considered internal to that package and should not be imported even by other mcstatus internals (except in tests).

Additionally, this PR tightens the public contract where appropriate by introducing __all__ declarations. By default, non-_ prefixed names are importable, but when __all__ is defined, only explicitly exported objects are considered public. This avoids confusion around helper constants or typing helpers (for example variables like T = TypeVar("T") or constants like MINECRAFT_COLOR_TO_RGB_JAVA). This approach allows type checkers like basedpyright to warn users when importing non-public symbols.

The changes this PR introduces in terms of public / private API separation were primarily based on what was or wasn't documented in the public API sphinx docs. That said, the documentation was actually missing some things that definitely should have been included. This PR fixes those instances too.

Specific, potentially breaking changes

  • Raw response typing (TypedDict classes) now live under an internal module name and are explicitly not public API. They were never documented as public, so no backwards compatibility is provided for these, however, they were previously importable directly from mcstatus.responses, which could have lead people to think these were public types. This PR will intentionally break these imports.
  • Networking internals (DNS & address logic) were moved out of the publicly reachable modules directly under mcstatus, to mcstatus._net package. Similarly, these were never intended to be public, this naming just makes that expectation clear. Again, no deprecations are provided here, people relying on these internals directly will face an immediate breakage.
  • The protocol package (originally only housing the protocol/connection.py file) was renamed to _protocol, making it clear that this is an internal util.
  • The client classes (previously inconsistently named, pinger/querier/status classes) were renamed and moved under the private _protocol package, with no deprecations provided for their original import paths.
  • Forge response data classes (mcstatus.forge_data) were not previously explicitly documented in the docs as public. This is however very odd, as those types are useful to people that rely on type annotations (which is why all of the other response classes are public). For clean code organization purposes, this module was moved into mcstatus.responses.forge, and even though it wasn't documented to be public, compatibility shims for old mcstatus.forge_data imports will be provided.

Follow-ups and open questions

Once merged, mcstatus should bump the major version to v13 (not immediately after). That said, before this PR can be merged, several questions need to be resolved:

  • Should the re-exported response classes from mcstatus/responses/__init__.py only be considered as something there for deprecation purposes, meaning using them should emit deprecation warnings, guiding users towards importing from the specific public submodules inside of the responses new package structure, or should they remain re-exported with the intention to stay re-exported? If they are to remain re-exported, should the forge response classes (which were never a part of responses.py and rather had their own file (forge_data.py) be kept reachable only udner the new mcstatus.responses.forge namespace, or should re-exports for it be included into mcstatus.responses directly? Additionally, if explicit re-exports are desired, should the structure of the package be considered entirely internal, meaning all of the submodules inside of mcstatus.responses become _ prefixed and not even meant for users to import directly?
    • Resolved: Responses will remain re-exported, forge responses will join these re-exports, fully-qualified imports of the submodules will become public API as well
  • Is the approach with using __all__ to separate out public vs private parts of modules sufficient, or should this PR simply add _ prefixes for everything within the public modules that isn't supposed to be public?
    • Resolved: The PR attempts to add the _ prefix to as many internal variables in public moduels as possible, to avoid confusion from users. However, due to it's runtime behavior with star imports, __all__ will remain defined everywhere (including where it previously wasn't).
    • The definitive answer to whether or not something is public API remains the docs, the intuitive answer is whether there's _ prefix anywhere in the fully qualified path of the given symbol, __all__ does not play a strict role as to whether or not something is public API for us, but anything that is public API should be present in __all__, and anything that isn't, shouldn't be.
  • Review whether additional deprecated paths should gain explicit compat shims, even if they point to now private internals before their complete removal. This might still be worth it since our public API contract was very poorly established, even if we always consider some of these parts to be mcstatus internals.
    • The main thing in question is whether the raw type-dict response types should gain deprecations.
    • Though this does also apply to true internals that people could've assumed were public, e.g. Connection,Address, pinger/status classes, ...
    • Resolved: Additional deprecations will not be included
  • Is the current documentation consistent and contains everything that should be public API, and nothing that is now private API (other than on the internal docs page which is explicitly for private API)?

Merge notes

  • This PR intentionally does not attempt to remove v13 deprecations. It is not meant to be a PR that prepares the mcstatus codebase for the v13 release, it just introduces the new file structure. Release preparations should be handled later, after this is merged, once we're ready to actually release v13.
  • Please use the rebase merging strategy when merging.
  • Do not merge the PR unless it is up-to-date with the latest commit in master (even without merge conflicts, with wide structure changes like these, the automatic merging might result in wrong import paths in various places, or badly placed newly created files, etc.)
  • Do not merge if there are still fixup! commits (github's rebase strategy doesn't perform --autosquash, needs to be done manually with a force-push once ready)

@ItsDrike ItsDrike added a: api Related to exposed core API of the project s: stalled Something is blocking further progress t: revision Complete or partial rewrite of something (code cleanup, performance improvements, etc.) m: breaking This introduces backwards compatibility breaking changes to the public API a: internal This only changes the internal behavior of mcstatus, it doesn't affect any part of the public API. labels Feb 8, 2026
@ItsDrike ItsDrike changed the title Restructure the project Restructure the project & public API clarification from code Feb 8, 2026
@ItsDrike ItsDrike force-pushed the restructure-project branch 2 times, most recently from fe07190 to c9713f3 Compare February 8, 2026 15:31
@PerchunPak
Copy link
Member

Should the re-exported response classes from mcstatus/responses/__init__.py only be considered as something there for deprecation purposes, meaning using them should emit deprecation warnings, guiding users towards importing from the specific public submodules inside of the responses new package structure, or should they remain re-exported with the intention to stay re-exported? If they are to remain re-exported, should the forge response classes (which were never a part of responses.py and rather had their own file (forge_data.py) be kept reachable only udner the new mcstatus.responses.forge namespace, or should re-exports for it be included into mcstatus.responses directly? Additionally, if explicit re-exports are desired, should the structure of the package be considered entirely internal, meaning all of the submodules inside of mcstatus.responses become _ prefixed and not even meant for users to import directly?

I think re-exports are very convenient, would keep it. Also, I don't think it is necessary to prefix all modules inside mcstatus/responses with _. The fact that forge stuff is not documented is definitely a mistake, it is a part of the public API. We should probably document those in a separate PR, but let's postpone any changes until ruff-select-all (to avoid merge conflicts)

Is the approach with using __all__ to separate out public vs private parts of modules sufficient, or should this PR simply add _ prefixes for everything within the public modules that isn't supposed to be public?

I would prefer the second option, so you don't have to remember __all__ when reading the code

@ItsDrike
Copy link
Member Author

ItsDrike commented Feb 8, 2026

The fact that forge stuff is not documented is definitely a mistake, it is a part of the public API. We should probably document those in a separate PR, but let's postpone any changes until ruff-select-all (to avoid merge conflicts)

I have already introduced the docs for it in this PR, as it does also clarifies the public API more concretely, but I can move it out into a separate one if desired.

I think re-exports are very convenient, would keep it. Also, I don't think it is necessary to prefix all modules inside mcstatus/responses with _.

Sure, I agree

I would prefer the second option, so you don't have to remember all when reading the code

__all__ is a valid way of handling this, and a type-checker can warn about misuse if configured properly, but I can make the change, it just might make some of the internals in those modules a little harder to read compared to them having "cleaner" constants.

@ItsDrike ItsDrike force-pushed the restructure-project branch 4 times, most recently from 6c79f27 to 4befe5e Compare February 10, 2026 20:48
@ItsDrike ItsDrike force-pushed the restructure-project branch 2 times, most recently from 3732af7 to d53012d Compare February 11, 2026 11:10
@PerchunPak
Copy link
Member

I have already introduced the docs for it in this PR, as it does also clarifies the public API more concretely, but I can move it out into a separate one if desired.

We most definitely should move that into a separate PR and release it in v12

@ItsDrike ItsDrike force-pushed the restructure-project branch from d53012d to 2ec4186 Compare February 11, 2026 11:25
Copy link
Member

@PerchunPak PerchunPak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of test_compat.py

@ItsDrike ItsDrike force-pushed the restructure-project branch from f9de07f to f33c86f Compare February 11, 2026 17:41
@ItsDrike ItsDrike marked this pull request as ready for review February 11, 2026 18:05
@ItsDrike ItsDrike added m: do-not-merge The PR can be reviewed, but cannot be merged now and removed s: stalled Something is blocking further progress labels Feb 11, 2026
Copy link
Member

@PerchunPak PerchunPak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@ItsDrike ItsDrike force-pushed the restructure-project branch 4 times, most recently from ffb435f to fb91214 Compare February 14, 2026 18:49
@ItsDrike ItsDrike mentioned this pull request Feb 14, 2026
This refactors the mcstatus codebase to clearly separate public API,
private internals, and deprecated compatibility paths. Additionally, it
improves naming of various internals for better consistency and clarity,
and splits some larger files into more manageable modules.

Major structural changes:

- Introduced private internal namespaces:
    - `_net` for networking and DNS utils
    - `_protocol` for protocol clients and connections
    - `_utils` for internal helpers
- Split the monolithic `responses.py` into a proper package. This
  separates the raw response models (type-dicts) into a single
  stand-alone module within this package, adds a module with shared base
  classes, and then specific response modules.
- Renamed protocol-facing classes to reflect their actual role
  (`ServerPinger` -> `JavaClient`, `BedrockServerStatus` ->
  `BedrockClient`, etc.)

Compatibility and deprecation handling:

- Added `mcstatus/_compat` with explicit compatibility shims for
  deprecated public modules.
- Compat files are injected at build time via hatch force-include
  directive, keeping the deprecated paths out of the source tree for
  cleaner file organization for contributors, while still providing the
  same accessible deprecations to end users of mcstatus.
- Added ruff rules to prevent accidental internal imports from
  `_compat`.

Behavioral and semantic adjustments:

- Raw response TypedDicts are now centralized under `responses._raw`
- MOTD transformers and simplifiers and no longer a part of the public
  API and are explicitly treated as internal.

Tests, docs and toolings:

- Updated all imports and test paths to reflect the new structure
- Adjusted sphinx config and docs for moved internals
These deprecations are no longer relevant as the entire module was
deprecated
@ItsDrike ItsDrike force-pushed the restructure-project branch from fb91214 to 520f582 Compare February 14, 2026 18:53
@ItsDrike ItsDrike merged commit 2f2556a into master Feb 14, 2026
10 checks passed
@ItsDrike ItsDrike deleted the restructure-project branch February 14, 2026 22:48
@ItsDrike ItsDrike removed the m: do-not-merge The PR can be reviewed, but cannot be merged now label Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: api Related to exposed core API of the project a: internal This only changes the internal behavior of mcstatus, it doesn't affect any part of the public API. m: breaking This introduces backwards compatibility breaking changes to the public API t: revision Complete or partial rewrite of something (code cleanup, performance improvements, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants