mcpField
P
python

python

github.com/imankulovDevelopment452 installsUpdated 3w ago
Repository

About python

python is a set of coding standards for modern Python 3.12+ projects. It is intended for developers seeking consistent typing, module organization, and layered application design.

Key features

  1. 01Domain-oriented module structure
  2. 02Layered dependencies and thin entry points
  3. 03Type annotations and modern syntax
  4. 04Naming and import conventions
  5. 05Exception and logging guidance

Use cases

  1. 01Organizing a new Python application
  2. 02Defining reusable business logic services
  3. 03Reviewing function signatures and type hints
  4. 04Reducing circular imports and module coupling

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/imankulov/skills --skill python

After installing, type/pythonin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
data-structures.md
docstrings.md
logical-grouping.md
module-organization.md
parallel-symmetry.md
url-parsing.md

Details

From the project README

Python Coding Standards

Opinionated conventions for modern Python (3.12+) emphasizing type safety, clear function signatures, consistent module organization, and a layered architecture.

App Architecture

Screaming Architecture

Name modules after the domain — billing/, authentication/, annual_reports/ — so the directory listing says what the app does rather than which framework it uses. Directories carry the domain; the layers below live in files inside them (billing/services.py, billing/api.py). A top-level models/ or views/ directory inverts that and scatters one domain across the tree.

Keep the modules flat — auth/, users/, and billing/ as siblings, not users/auth/ and users/billing/. Nesting buries domains a level down, so the top-level listing stops telling you at a glance what the app is about.

Layers

Dependencies flow downward. Each layer imports from the one below it, never the reverse.

+-------------------------------------------------------+
|  api.py, tasks.py, admin.py, ...  (entry points)      |
+-------------------------------------------------------+
                             | imports
                             v
+-------------------------------------------------------+
|             services.py  (business logic)             |
+-------------------------------------------------------+
                             | imports
                             v
+-------------------------------------------------------+
|      models.py, types.py, const.py  (data layer)      |
+-------------------------------------------------------+

Entry points (api.py, tasks.py, admin.py, cli.py, etc.) are thin wrappers that call service functions. They handle request parsing, scheduling, and output formatting but contain no business logic. Entry points never import each other — they are peers that all depend on services; cross-imports create circular dependencies and tight coupling.

services.py holds all business logic. Service functions are reusable across any entry point — an API endpoint, a background task, a CLI command, and an admin action can all call the same service function.

A service takes structured input and returns structured output. It may do whatever I/O the work requires — read a file it was given a path to, query the database, call a third-party API — but it never touches the user-facing edge: no prompting or argument parsing, no coercing raw strings into typed values, no reaching into an HTTP request or another framework object, no formatting results for display. All of that belongs in the wrapper.

Data layer (models.py, types.py, const.py) defines data structures with zero business logic. const.py must have no internal imports — it's safe to import from anywhere.

Put all imports at the top of the module. Defer an import inside a function only to break a circular dependency; needing this often means the module structure needs refactoring.

Entry Point Naming Convention

Service functions own the base name. Entry points that wrap them add a suffix for their layer, so from myapp.services import create_user works everywhere without aliasing.

LayerSuffixExample
Service (business logic)(none)create_user()
API endpoint_apicreate_user_api()
CLI command_clicreate_user_cli()
Background task_taskcreate_user_task()

Functions

  • Type-annotate parameters and return values when the type carries information. If the only honest type is Any (a wrapper around an optional/dynamically-imported library, a generic decorator, a **kwargs passthrough), leave it unannotated — Any adds noise without helping the reader, the type checker, or the IDE.
  • Docstrings carry no type information — types live in the signature. See Docstrings and comments for how much to write.
  • For functions with more than 3 parameters, use keyword-only arguments (*).
  • Prefix internal helper functions with underscore and keep their docstrings to one line.
  • Inline a helper that has one caller, unless it holds logic worth testing on its own — a predicate, a mapper, a conversion. Naming those lets a test drive them with hand-built inputs instead of setting up everything the caller needs; otherwise a second name for one function's body buys nothing.
  • Order a file top-down like a newspaper (Robert C. Martin's Clean Code): public functions first, helpers below, a single-caller _helper directly under its caller. Group Pydantic models near the top so they're defined before use.

The extended docstring form below is what a function looks like once it has earned all three sections; most functions stop at the one-line summary.

def process_user_data(*, user_id: int, name: str, email: str, age: int) -> UserData:
    """Process and validate user data.

    Args:
        user_id: Unique identifier for the user
        name: User's full name
        email: User's email address
        age: User's age in years

    Returns:
        Processed user data with validation status

    Raises:
        ValueError: If the email is malformed or the age is negative
    """
    ...

def _format_name(first: str, last: str) -> str:
    """Combine first and last name into full name."""
    return f"{first} {last}".strip()

Exceptions

Service functions signal failure by raising. List every exception a function raises on purpose in the docstring's Raises: section — that contract is what entry points read to decide what to catch and how to translate it.

Reach for the built-ins first: ValueError for bad input, RuntimeError for a broken invariant. When the app needs its own, define one hierarchy in myapp/exceptions.py — beside the app's __init__.py, a level above the modules that raise them — with MyAppError as the base and MyAppAuthenticationError and siblings under it. Modules don't get their own exceptions.py, and a failure mode rarely earns its own class; callers catch the base far more often than the leaf.

Leading Underscores

A single leading underscore says "internal — don't call or import this from outside this module." Reserve it for two cases where outside use would cause a real problem:

  • Internal functions and methods. A _helper may skip validation the public API enforces, hold an invariant of the surrounding function, or change shape at any time.
  • Mutable singleton state used for lazy initialization (_queue, _started, _patched, _server_url). Outside code mutating it would break the module's invariants.

If another module needs to import it, it's not internal — drop the underscore. from foo import _bar is a lie: either keep the function truly internal, or move it to a shared module and remove the prefix. When extracting a helper into a shared utils.py, drop the underscore — it was internal to the old module, not the new one.

Do not prefix module-level constants or classes. UPPER_CASE already conveys "constant"; class privacy comes from where the class lives (don't re-export it, keep it next to its caller). Use MAX_RETRIES and RequestTracer, not _MAX_RETRIES or _RequestTracer.

Dunders (__init__, __enter__, …) are Python's protocol, not this convention.

Import Hygiene

Import your own modules under their real name — aliasing internal code hides where a symbol came from. When a service function and the entry point wrapping it would collide, apply the layer suffix rather than import ... as. Reserve aliases for a genuine collision between two third-party imports and for their conventional shorthands (import pandas as pd).

Shared utilities belong in a utility module. When a second module needs a helper first written inside another module, move it to a shared location (utils.py, helpers.py). Don't let module_b depend on module_a just because module_a defined a generic helper first.

Type Hints (PEP 604 and 695)

Use modern union syntax (list[str], dict[str, int] | None). Never import List, Dict, Optional, or Union from typing for basic types.

Declare generics with PEP 695 type parameters — class Codec[T]:, def first[T](items: list[T]) -> T: — not TypeVar plus Generic.

Naming

The same rules cover variables, functions, classes, packages, and module filenames.

Write words out. Prefer document to doc, request_id to req_id, configuration to cfg. Dropped vowels and truncations save a few keystrokes and cost every later reader a guess. The exceptions are the short forms that are more familiar than what they stand for — id, url, json, csv, http — and even those go unabbreviated when there's any doubt.

Always separate words. snake_case for variables, functions, and module filenames; CamelCase for classes. Never run two words together without a boundary: parse_payload, not parsepayload; RetryPolicy, not Retrypolicy; sync_state.py, not syncstate.py.

Treat abbreviations as single words in CamelCase, capitalizing only the first letter: JsonParser, ApiClient, DbConnection, HttpResponse, SqlQuery — not JSONParser.

Logging

Follow the project's logging conventions when its instructions define one. Otherwise use the stdlib logging module and pass values as lazy %s arguments rather than interpolating them: logger.warning("Vector search failed: %s", exc), not an f-string.

References

For detailed patterns on specific topics, load these as needed:

Verification

After writing Python code, verify the two rules that live in references and are easiest to miss:

  • Fields, constants, and processing steps grouped by concern, in the same order everywhere
  • Systems processing the same input in parallel share prefixes, guards, and data shapes

Comments

Markdown supported