Metadata-Version: 2.4
Name: norpcore
Version: 0.1.0
Summary: NORP-Core: a pure, domain-neutral meta-framework kernel (slot connector, address resolver, registry, event bus) with zero upper-layer dependencies.
Author-email: xingluosama121 <gzyzhxx@outlook.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/xingluosama121/norp-core
Project-URL: Repository, https://github.com/xingluosama121/norp-core
Project-URL: Issues, https://github.com/xingluosama121/norp-core/issues
Project-URL: Source, https://github.com/xingluosama121/norp-core
Keywords: meta-framework,kernel,plugin,dependency-injection,event-bus,registry,hooks,ioc
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Provides-Extra: release
Requires-Dist: build>=1.2; extra == "release"
Requires-Dist: twine>=5.0; extra == "release"
Dynamic: license-file

# NORP-Core

**A pure, domain-neutral meta-framework kernel.**

NORP-Core is a minimal kernel for assembling software out of pluggable parts. It
assumes **no upper-layer implementation** and **no particular domain**: it is the
four non-replaceable components plus the registration triad and the machinery
they need. Any program - a service, a CLI, a game loop, a compiler, a build tool
- can build on it and declare its own slots, hook layers, event vocabulary and
component kinds.

The direction of dependency is strictly one-way:

```
        host / application   (any concrete software)
                  |
                  v
              NORP-Core
                  |
                  v
          Python standard library
```

NORP-Core never imports an upper layer.

## The kernel

Exactly **four non-replaceable components**, plus supporting machinery:

| # | Component | Module | Responsibility |
|---|-----------|--------|----------------|
| 1 | Slot connector | `norp_core.layer.ArchLayer` | assemble slot values into implementations, hot-mount |
| 2 | Address resolver | `norp_core.address.resolve_address` | turn an address string into an object |
| 3 | Registry | `norp_core.registry.Registry` | generic `kind -> name -> object` store |
| 4 | Event bus | `norp_core.events.EventBus` | the only decoupling point |

Supporting machinery:

- `norp_core.slots` - the slot **table** (mechanism only; ships no slots);
- `norp_core.hooks` - the hook **mechanism** (`HookSystem` / `HookLayer` /
  `Hook` / `BoundHook`; ships no standard hook layers);
- the registration triad `register_slot` / `register_layer` / `register_hook`.

## Domain neutrality

The kernel understands only generic software concepts - a *slot*, a *hook*, an
*event*, an *address*, a *layer*, a *registry*. It contains **no** domain
vocabulary and **no** baked-in assumption about what is being assembled:

| Guarantee | How |
|---|---|
| no fixed event vocabulary | event names are free-form strings chosen by the host |
| no pre-defined slots | `SLOT_SPECS` starts empty; the host calls `register_slot` |
| no standard hook layers | `HookSystem.standard_layers()` returns `[]` |
| no assumed component kinds | `Registry` is generic `kind -> name -> object` |
| no special slot names baked in | string passthrough is opt-in via `register_passthrough_slot` |
| no special dict-value handling | callables in dict values are controlled per-slot by `SlotSpec.call_dict_factories` |
| no host concept of its own | `Registry.extras` is an opaque host extension point the kernel never inspects |

An upper layer that wants any of the above simply declares it:

```python
# a host declares its own slots (the kernel ships none)
core.register_slot(core.SlotSpec(
    name="storage",
    description="where data is persisted",
    protocol="StorageProtocol",
    string_semantics="name_or_address",
    call_dict_factories=False,      # this slot's dict values are callbacks
), builtin=True)

# a host declares its own hook layers
core.register_layer(name="net", order=100,
                    hooks={"before_request": {"mutating": True}})

# a host declares its own event topics just by emitting them
bus.emit("before_request", url="...")
```

## Layout

```
core/
  pyproject.toml          # distribution: norpcore (import name norp_core)
  README.md               # this file
  LICENSE                 # MIT
  publish_pypi.ps1        # build + PyPI publish helper (PowerShell, primary)
  release.ps1             # build + PyPI publish helper (PowerShell, legacy)
  release.py              # build + PyPI publish helper (Python, legacy)
  .gitignore
  norp_core/
    __init__.py           # the whole public surface
    py.typed              # PEP 561 typing marker
    events.py             # component 4: EventBus / Event / HookVeto
    registry.py           # component 3: Registry / ComponentError
    address.py            # component 2: resolve_address / is_address_like / AddressError
    layer.py              # component 1: ArchLayer / call_factory / register_passthrough_slot
    slots.py              # slot table mechanism (no built-in slots)
    hooks.py              # hook mechanism + register_layer / register_hook
  tests/
    test_norp_core.py     # functional tests for all four components + triad
    test_purity.py        # import-graph purity: NORP-Core leaks no upper layer
```

## Quick start

```python
import norp_core as core

# 1. declare slots (NORP-Core ships none)
core.register_slot(core.SlotSpec(
    name="service",
    description="a pluggable service",
    protocol="ServiceProtocol",
    string_semantics="name_or_address",
))

# 2. register components by kind (kinds are chosen by the host)
reg = core.Registry()
reg.register("service", "mock", lambda: MyMockService())

# 3. assemble
layer = core.ArchLayer(service="mock")
layer.set_default("service", lambda ctx: reg.build("service", "mock"))
layer.connect()
svc = layer["service"]

# 4. wire the hooks / event bus
system = core.HookSystem(reg.bus)
core.register_layer(name="L1", hooks={"before_start": {"mutating": True}},
                    system=system)
core.register_hook("before_start", my_guard, system=system)
```

## Testing

```bash
cd core
python tests/test_norp_core.py     # functional
python tests/test_purity.py        # purity (no upper-layer imports)
# or, with pytest installed:
pytest -q
```

`test_purity.py` launches a fresh interpreter, imports `norp_core`, and asserts
that no top-level module of an upper layer appears in `sys.modules`, and that
every newly imported module is either the standard library or `norp_core` itself.
It also statically scans the sources so a lazy import cannot sneak in.

## Releasing

The distribution name is **`norpcore`** (the import name stays `norp_core`).

`publish_pypi.ps1` is the primary one-command build-and-publish helper. It:

0. checks version consistency between `pyproject.toml` and `norp_core/__init__.py`;
1. checks the Python / `build` / `twine` environment;
2. removes stale `dist/` artifacts of the SAME version;
3. builds the sdist and wheel with `python -m build`;
4. verifies the built wheel carries the declared version;
5. validates the packages with `twine check`;
6. resolves credentials, then uploads to the chosen index.

### PowerShell

```powershell
pip install -e ".[release]"                    # build + twine

.\publish_pypi.ps1 -CheckOnly                  # build + validate, no upload
.\publish_pypi.ps1 -DryRun                     # preview the upload command
.\publish_pypi.ps1 -Target testpypi -Token "pypi-xxxxxxxx"
.\publish_pypi.ps1 -Target pypi     -Token "pypi-xxxxxxxx" -Force
```

Pass the publish token straight in with `-Token` to customise the credential for
a single run; if `-Token` is omitted the script reads `PYPI_TOKEN` /
`TESTPYPI_TOKEN` / `TWINE_PASSWORD` and finally falls back to `~/.pypirc`, or
prompts for a **masked** token. The token is never written to disk or echoed.

Other flags: `-SkipBuild`, `-SkipExisting`, `-Force` (skip the confirmation).
If Windows blocks the script, run it with
`powershell -ExecutionPolicy Bypass -File .\publish_pypi.ps1 ...`.

### Legacy helpers

`release.ps1` and `release.py` are the earlier equivalent helpers (test suite +
clean + build + `twine check` + optional upload). Credentials there are read from
`PYPI_API_TOKEN` / `TESTPYPI_API_TOKEN` or `~/.pypirc`:

```powershell
.\release.ps1 -BuildOnly
.\release.ps1 -Target pypi -Token "pypi-xxxxxxxx" -Yes
```

```bash
python release.py --build-only
python release.py --target pypi
```

The distribution is licensed under the **MIT License** — see [`LICENSE`](LICENSE).
