winterforge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- winterforge-0.1.0/PKG-INFO +19 -0
- winterforge-0.1.0/README.md +201 -0
- winterforge-0.1.0/pyproject.toml +96 -0
- winterforge-0.1.0/setup.cfg +4 -0
- winterforge-0.1.0/tests/test_acceptance.py +331 -0
- winterforge-0.1.0/tests/test_canonical_resolution.py +310 -0
- winterforge-0.1.0/tests/test_e2e_integration.py +287 -0
- winterforge-0.1.0/tests/test_initialization.py +215 -0
- winterforge-0.1.0/tests/test_user_system_integration.py +321 -0
- winterforge-0.1.0/winterforge/__init__.py +144 -0
- winterforge-0.1.0/winterforge/frags/__init__.py +35 -0
- winterforge-0.1.0/winterforge/frags/base.py +810 -0
- winterforge-0.1.0/winterforge/frags/composition.py +322 -0
- winterforge-0.1.0/winterforge/frags/field_helpers.py +194 -0
- winterforge-0.1.0/winterforge/frags/injectable.py +180 -0
- winterforge-0.1.0/winterforge/frags/manifest.py +636 -0
- winterforge-0.1.0/winterforge/frags/materialization.py +124 -0
- winterforge-0.1.0/winterforge/frags/mixins/__init__.py +5 -0
- winterforge-0.1.0/winterforge/frags/mixins/trait_mixin.py +112 -0
- winterforge-0.1.0/winterforge/frags/primitives/__init__.py +10 -0
- winterforge-0.1.0/winterforge/frags/primitives/_base.py +158 -0
- winterforge-0.1.0/winterforge/frags/primitives/affinity.py +108 -0
- winterforge-0.1.0/winterforge/frags/primitives/manager.py +88 -0
- winterforge-0.1.0/winterforge/frags/primitives/trait.py +96 -0
- winterforge-0.1.0/winterforge/frags/registries/__init__.py +5 -0
- winterforge-0.1.0/winterforge/frags/registries/affinity_registry.py +96 -0
- winterforge-0.1.0/winterforge/frags/registries/application_registry.py +56 -0
- winterforge-0.1.0/winterforge/frags/registries/config_registry.py +316 -0
- winterforge-0.1.0/winterforge/frags/registries/field_registry.py +218 -0
- winterforge-0.1.0/winterforge/frags/registries/frag_registry.py +592 -0
- winterforge-0.1.0/winterforge/frags/registries/permission_registry.py +170 -0
- winterforge-0.1.0/winterforge/frags/registries/profile_registry.py +153 -0
- winterforge-0.1.0/winterforge/frags/registries/role_registry.py +324 -0
- winterforge-0.1.0/winterforge/frags/registries/session_registry.py +198 -0
- winterforge-0.1.0/winterforge/frags/registries/trait_registry.py +96 -0
- winterforge-0.1.0/winterforge/frags/registries/user_registry.py +542 -0
- winterforge-0.1.0/winterforge/frags/scope.py +379 -0
- winterforge-0.1.0/winterforge/frags/traits/__init__.py +21 -0
- winterforge-0.1.0/winterforge/frags/traits/_field_info.py +130 -0
- winterforge-0.1.0/winterforge/frags/traits/_manager.py +413 -0
- winterforge-0.1.0/winterforge/frags/traits/api_builder.py +288 -0
- winterforge-0.1.0/winterforge/frags/traits/authenticatable.py +156 -0
- winterforge-0.1.0/winterforge/frags/traits/authorizable.py +230 -0
- winterforge-0.1.0/winterforge/frags/traits/budget_aware.py +107 -0
- winterforge-0.1.0/winterforge/frags/traits/cache_aware.py +116 -0
- winterforge-0.1.0/winterforge/frags/traits/clearable.py +22 -0
- winterforge-0.1.0/winterforge/frags/traits/cli_result.py +82 -0
- winterforge-0.1.0/winterforge/frags/traits/composable.py +237 -0
- winterforge-0.1.0/winterforge/frags/traits/emits_events.py +209 -0
- winterforge-0.1.0/winterforge/frags/traits/fieldable.py +623 -0
- winterforge-0.1.0/winterforge/frags/traits/has_data_source.py +111 -0
- winterforge-0.1.0/winterforge/frags/traits/has_data_target.py +140 -0
- winterforge-0.1.0/winterforge/frags/traits/has_permission.py +174 -0
- winterforge-0.1.0/winterforge/frags/traits/has_result.py +103 -0
- winterforge-0.1.0/winterforge/frags/traits/has_roles.py +239 -0
- winterforge-0.1.0/winterforge/frags/traits/http_request.py +112 -0
- winterforge-0.1.0/winterforge/frags/traits/http_response.py +86 -0
- winterforge-0.1.0/winterforge/frags/traits/index_generator.py +407 -0
- winterforge-0.1.0/winterforge/frags/traits/injectable.py +49 -0
- winterforge-0.1.0/winterforge/frags/traits/is_application.py +109 -0
- winterforge-0.1.0/winterforge/frags/traits/is_dirty.py +126 -0
- winterforge-0.1.0/winterforge/frags/traits/last_match.py +57 -0
- winterforge-0.1.0/winterforge/frags/traits/manifest.py +22 -0
- winterforge-0.1.0/winterforge/frags/traits/ownable.py +110 -0
- winterforge-0.1.0/winterforge/frags/traits/permissioned.py +155 -0
- winterforge-0.1.0/winterforge/frags/traits/persistable.py +442 -0
- winterforge-0.1.0/winterforge/frags/traits/profileable.py +211 -0
- winterforge-0.1.0/winterforge/frags/traits/queryable.py +101 -0
- winterforge-0.1.0/winterforge/frags/traits/registry.py +27 -0
- winterforge-0.1.0/winterforge/frags/traits/sessionable.py +188 -0
- winterforge-0.1.0/winterforge/frags/traits/sluggable.py +162 -0
- winterforge-0.1.0/winterforge/frags/traits/timestamped.py +162 -0
- winterforge-0.1.0/winterforge/frags/traits/titled.py +150 -0
- winterforge-0.1.0/winterforge/frags/traits/tokenable.py +88 -0
- winterforge-0.1.0/winterforge/frags/traits/typed.py +195 -0
- winterforge-0.1.0/winterforge/frags/traits/userable.py +126 -0
- winterforge-0.1.0/winterforge/frags/traits/with_history.py +282 -0
- winterforge-0.1.0/winterforge/installer/__init__.py +9 -0
- winterforge-0.1.0/winterforge/installer/bootstrap_config.py +86 -0
- winterforge-0.1.0/winterforge/installer/commands.py +858 -0
- winterforge-0.1.0/winterforge/installer/init_command.py +71 -0
- winterforge-0.1.0/winterforge/installer/profile_installer.py +319 -0
- winterforge-0.1.0/winterforge/installer/profiles.py +243 -0
- winterforge-0.1.0/winterforge/plugins/__init__.py +133 -0
- winterforge-0.1.0/winterforge/plugins/_base.py +483 -0
- winterforge-0.1.0/winterforge/plugins/_discovery.py +278 -0
- winterforge-0.1.0/winterforge/plugins/_manager_manager.py +109 -0
- winterforge-0.1.0/winterforge/plugins/_matchable.py +61 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/__init__.py +143 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/authentication.py +52 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/bootstrapper.py +41 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/counter.py +60 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/email_provider.py +63 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/hashing.py +55 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/http.py +298 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/identity.py +72 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/output_redirect.py +29 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/query_execution.py +142 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/resolver.py +74 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/session.py +78 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/status_printer.py +57 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/storage.py +198 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/token_provider.py +80 -0
- winterforge-0.1.0/winterforge/plugins/_protocols/validator.py +32 -0
- winterforge-0.1.0/winterforge/plugins/auditors/__init__.py +9 -0
- winterforge-0.1.0/winterforge/plugins/auditors/_manager.py +24 -0
- winterforge-0.1.0/winterforge/plugins/auditors/orphaned_fields/__init__.py +224 -0
- winterforge-0.1.0/winterforge/plugins/authentication/__init__.py +9 -0
- winterforge-0.1.0/winterforge/plugins/authentication/manager.py +64 -0
- winterforge-0.1.0/winterforge/plugins/authentication/password_provider.py +107 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/__init__.py +8 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/application.py +127 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/bootstrap.py +28 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/bootstrapper_manager.py +42 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/database.py +97 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/env_var.py +97 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/framework_configuration.py +83 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/manager.py +139 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/materialization.py +60 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/plugin_configuration.py +66 -0
- winterforge-0.1.0/winterforge/plugins/bootstrap/yaml.py +63 -0
- winterforge-0.1.0/winterforge/plugins/cache/__init__.py +6 -0
- winterforge-0.1.0/winterforge/plugins/cache/_backend.py +126 -0
- winterforge-0.1.0/winterforge/plugins/cache/_listeners.py +49 -0
- winterforge-0.1.0/winterforge/plugins/cache/_manager.py +182 -0
- winterforge-0.1.0/winterforge/plugins/cache/lru/__init__.py +195 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/__init__.py +13 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/lazy.py +52 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/manager.py +92 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/none.py +48 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/protocol.py +39 -0
- winterforge-0.1.0/winterforge/plugins/cache_warmers/scheduled.py +79 -0
- winterforge-0.1.0/winterforge/plugins/cli/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/cli/__main__.py +6 -0
- winterforge-0.1.0/winterforge/plugins/cli/_cli_commands.py +248 -0
- winterforge-0.1.0/winterforge/plugins/cli/_context.py +92 -0
- winterforge-0.1.0/winterforge/plugins/cli/_manager.py +466 -0
- winterforge-0.1.0/winterforge/plugins/cli/_validation.py +116 -0
- winterforge-0.1.0/winterforge/plugins/cli/main.py +175 -0
- winterforge-0.1.0/winterforge/plugins/counter/__init__.py +11 -0
- winterforge-0.1.0/winterforge/plugins/counter/database_synced.py +68 -0
- winterforge-0.1.0/winterforge/plugins/counter/fresh.py +49 -0
- winterforge-0.1.0/winterforge/plugins/counter/manager.py +56 -0
- winterforge-0.1.0/winterforge/plugins/data/__init__.py +8 -0
- winterforge-0.1.0/winterforge/plugins/data/_manager.py +24 -0
- winterforge-0.1.0/winterforge/plugins/data/yaml/__init__.py +664 -0
- winterforge-0.1.0/winterforge/plugins/decorators/__init__.py +928 -0
- winterforge-0.1.0/winterforge/plugins/decorators/_configs.py +163 -0
- winterforge-0.1.0/winterforge/plugins/decorators/_decorator_base.py +224 -0
- winterforge-0.1.0/winterforge/plugins/decorators/_http_endpoint_config.py +96 -0
- winterforge-0.1.0/winterforge/plugins/decorators/_manager.py +83 -0
- winterforge-0.1.0/winterforge/plugins/decorators/_provider.py +115 -0
- winterforge-0.1.0/winterforge/plugins/decorators/bootstrapper.py +54 -0
- winterforge-0.1.0/winterforge/plugins/decorators/cli_command.py +251 -0
- winterforge-0.1.0/winterforge/plugins/decorators/counter_provider.py +69 -0
- winterforge-0.1.0/winterforge/plugins/decorators/emits.py +225 -0
- winterforge-0.1.0/winterforge/plugins/decorators/http_endpoint.py +200 -0
- winterforge-0.1.0/winterforge/plugins/decorators/openapi.py +265 -0
- winterforge-0.1.0/winterforge/plugins/decorators/output_redirect.py +38 -0
- winterforge-0.1.0/winterforge/plugins/decorators/provider_type.py +275 -0
- winterforge-0.1.0/winterforge/plugins/decorators/root.py +172 -0
- winterforge-0.1.0/winterforge/plugins/decorators/scope.py +41 -0
- winterforge-0.1.0/winterforge/plugins/decorators/status_printer.py +46 -0
- winterforge-0.1.0/winterforge/plugins/decorators/version_root.py +94 -0
- winterforge-0.1.0/winterforge/plugins/email/__init__.py +11 -0
- winterforge-0.1.0/winterforge/plugins/email/console_provider.py +63 -0
- winterforge-0.1.0/winterforge/plugins/email/manager.py +67 -0
- winterforge-0.1.0/winterforge/plugins/email/smtp_provider.py +107 -0
- winterforge-0.1.0/winterforge/plugins/envoy/__init__.py +6 -0
- winterforge-0.1.0/winterforge/plugins/envoy/_manager.py +30 -0
- winterforge-0.1.0/winterforge/plugins/envoy/_protocol.py +178 -0
- winterforge-0.1.0/winterforge/plugins/envoy/claude.py +295 -0
- winterforge-0.1.0/winterforge/plugins/envoy/local_ollama.py +259 -0
- winterforge-0.1.0/winterforge/plugins/events/__init__.py +13 -0
- winterforge-0.1.0/winterforge/plugins/events/dispatcher_manager.py +65 -0
- winterforge-0.1.0/winterforge/plugins/events/dispatchers/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/events/dispatchers/event.py +60 -0
- winterforge-0.1.0/winterforge/plugins/events/listener_manager.py +143 -0
- winterforge-0.1.0/winterforge/plugins/events/provider_manager.py +44 -0
- winterforge-0.1.0/winterforge/plugins/exporters/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/exporters/manager.py +38 -0
- winterforge-0.1.0/winterforge/plugins/exporters/yaml_file.py +120 -0
- winterforge-0.1.0/winterforge/plugins/extensions/__init__.py +8 -0
- winterforge-0.1.0/winterforge/plugins/extensions/_installer.py +182 -0
- winterforge-0.1.0/winterforge/plugins/extensions/_manager.py +25 -0
- winterforge-0.1.0/winterforge/plugins/hashing/__init__.py +11 -0
- winterforge-0.1.0/winterforge/plugins/hashing/argon2_provider.py +77 -0
- winterforge-0.1.0/winterforge/plugins/hashing/bcrypt_provider.py +69 -0
- winterforge-0.1.0/winterforge/plugins/hashing/manager.py +75 -0
- winterforge-0.1.0/winterforge/plugins/http_app/__init__.py +10 -0
- winterforge-0.1.0/winterforge/plugins/http_app/fastapi_provider.py +532 -0
- winterforge-0.1.0/winterforge/plugins/http_app/manager.py +62 -0
- winterforge-0.1.0/winterforge/plugins/http_authenticator/__init__.py +17 -0
- winterforge-0.1.0/winterforge/plugins/http_authenticator/basic_authenticator.py +78 -0
- winterforge-0.1.0/winterforge/plugins/http_authenticator/bearer_authenticator.py +78 -0
- winterforge-0.1.0/winterforge/plugins/http_authenticator/manager.py +76 -0
- winterforge-0.1.0/winterforge/plugins/http_request/__init__.py +17 -0
- winterforge-0.1.0/winterforge/plugins/http_request/form_handler.py +95 -0
- winterforge-0.1.0/winterforge/plugins/http_request/json_handler.py +105 -0
- winterforge-0.1.0/winterforge/plugins/http_request/manager.py +68 -0
- winterforge-0.1.0/winterforge/plugins/http_response/__init__.py +17 -0
- winterforge-0.1.0/winterforge/plugins/http_response/json_handler.py +91 -0
- winterforge-0.1.0/winterforge/plugins/http_response/manager.py +73 -0
- winterforge-0.1.0/winterforge/plugins/http_response/xml_handler.py +146 -0
- winterforge-0.1.0/winterforge/plugins/identity/__init__.py +21 -0
- winterforge-0.1.0/winterforge/plugins/identity/email_resolver.py +37 -0
- winterforge-0.1.0/winterforge/plugins/identity/frag_id_resolver.py +58 -0
- winterforge-0.1.0/winterforge/plugins/identity/frag_resolver.py +66 -0
- winterforge-0.1.0/winterforge/plugins/identity/manager.py +134 -0
- winterforge-0.1.0/winterforge/plugins/identity/slug_resolver.py +78 -0
- winterforge-0.1.0/winterforge/plugins/identity/title_resolver.py +54 -0
- winterforge-0.1.0/winterforge/plugins/identity/username_resolver.py +41 -0
- winterforge-0.1.0/winterforge/plugins/identity/uuid_resolver.py +92 -0
- winterforge-0.1.0/winterforge/plugins/importers/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/importers/manager.py +37 -0
- winterforge-0.1.0/winterforge/plugins/importers/yaml_file.py +82 -0
- winterforge-0.1.0/winterforge/plugins/indexes/__init__.py +6 -0
- winterforge-0.1.0/winterforge/plugins/indexes/_backend.py +144 -0
- winterforge-0.1.0/winterforge/plugins/indexes/_manager.py +124 -0
- winterforge-0.1.0/winterforge/plugins/indexes/composite/__init__.py +223 -0
- winterforge-0.1.0/winterforge/plugins/indexes/hash/__init__.py +160 -0
- winterforge-0.1.0/winterforge/plugins/initializers/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/initializers/application.py +91 -0
- winterforge-0.1.0/winterforge/plugins/initializers/manager.py +67 -0
- winterforge-0.1.0/winterforge/plugins/input_prompts/__init__.py +31 -0
- winterforge-0.1.0/winterforge/plugins/input_prompts/interactive_cli.py +262 -0
- winterforge-0.1.0/winterforge/plugins/input_prompts/manager.py +66 -0
- winterforge-0.1.0/winterforge/plugins/input_prompts/protocols.py +88 -0
- winterforge-0.1.0/winterforge/plugins/input_prompts/silent.py +173 -0
- winterforge-0.1.0/winterforge/plugins/lifecycle.py +143 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/bootstrap.py +23 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/debug.py +74 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/default.py +84 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/json_formatter.py +77 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/manager.py +120 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/quiet.py +61 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/source/__init__.py +7 -0
- winterforge-0.1.0/winterforge/plugins/output_formatters/source/formatter_source.py +25 -0
- winterforge-0.1.0/winterforge/plugins/output_redirects/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/output_redirects/cli_output_redirect.py +27 -0
- winterforge-0.1.0/winterforge/plugins/output_redirects/manager.py +88 -0
- winterforge-0.1.0/winterforge/plugins/output_redirects/null_output_redirect.py +34 -0
- winterforge-0.1.0/winterforge/plugins/permissions/__init__.py +7 -0
- winterforge-0.1.0/winterforge/plugins/permissions/provider_manager.py +45 -0
- winterforge-0.1.0/winterforge/plugins/query/__init__.py +9 -0
- winterforge-0.1.0/winterforge/plugins/query/_manager.py +23 -0
- winterforge-0.1.0/winterforge/plugins/query/_operators.py +52 -0
- winterforge-0.1.0/winterforge/plugins/query/_repository.py +510 -0
- winterforge-0.1.0/winterforge/plugins/query/affinity.py +45 -0
- winterforge-0.1.0/winterforge/plugins/query/composition.py +57 -0
- winterforge-0.1.0/winterforge/plugins/query/condition/__init__.py +65 -0
- winterforge-0.1.0/winterforge/plugins/query/condition_group/__init__.py +118 -0
- winterforge-0.1.0/winterforge/plugins/query/execution/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/query/execution/_manager.py +45 -0
- winterforge-0.1.0/winterforge/plugins/query/execution/index/__init__.py +168 -0
- winterforge-0.1.0/winterforge/plugins/query/execution/memory/__init__.py +211 -0
- winterforge-0.1.0/winterforge/plugins/query/execution/storage/__init__.py +183 -0
- winterforge-0.1.0/winterforge/plugins/query/executor/__init__.py +135 -0
- winterforge-0.1.0/winterforge/plugins/query/join/__init__.py +71 -0
- winterforge-0.1.0/winterforge/plugins/query/limit/__init__.py +45 -0
- winterforge-0.1.0/winterforge/plugins/query/offset/__init__.py +45 -0
- winterforge-0.1.0/winterforge/plugins/query/orphaned/__init__.py +46 -0
- winterforge-0.1.0/winterforge/plugins/query/scope.py +73 -0
- winterforge-0.1.0/winterforge/plugins/query/sort/__init__.py +52 -0
- winterforge-0.1.0/winterforge/plugins/query/trait.py +45 -0
- winterforge-0.1.0/winterforge/plugins/repository.py +572 -0
- winterforge-0.1.0/winterforge/plugins/resolvers/__init__.py +5 -0
- winterforge-0.1.0/winterforge/plugins/resolvers/manager.py +41 -0
- winterforge-0.1.0/winterforge/plugins/session/__init__.py +11 -0
- winterforge-0.1.0/winterforge/plugins/session/frag_provider.py +151 -0
- winterforge-0.1.0/winterforge/plugins/session/jwt_provider.py +125 -0
- winterforge-0.1.0/winterforge/plugins/session/manager.py +132 -0
- winterforge-0.1.0/winterforge/plugins/slug_convertors/__init__.py +13 -0
- winterforge-0.1.0/winterforge/plugins/slug_convertors/bootstrap.py +20 -0
- winterforge-0.1.0/winterforge/plugins/slug_convertors/manager.py +115 -0
- winterforge-0.1.0/winterforge/plugins/slug_convertors/python_slug.py +42 -0
- winterforge-0.1.0/winterforge/plugins/slug_convertors/sql_slug.py +49 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/__init__.py +10 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/boolean_result_printer.py +138 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/detailed_user_printer.py +85 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/list_frag_printer.py +74 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/manager.py +122 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/none_result_printer.py +77 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/single_frag_printer.py +134 -0
- winterforge-0.1.0/winterforge/plugins/status_printers/string_result_printer.py +107 -0
- winterforge-0.1.0/winterforge/plugins/storage/__init__.py +18 -0
- winterforge-0.1.0/winterforge/plugins/storage/_sql_base.py +327 -0
- winterforge-0.1.0/winterforge/plugins/storage/env.py +330 -0
- winterforge-0.1.0/winterforge/plugins/storage/manager.py +24 -0
- winterforge-0.1.0/winterforge/plugins/storage/postgresql.py +751 -0
- winterforge-0.1.0/winterforge/plugins/storage/sqlite.py +1081 -0
- winterforge-0.1.0/winterforge/plugins/storage/sqlite_old.py +999 -0
- winterforge-0.1.0/winterforge/plugins/storage/yaml.py +475 -0
- winterforge-0.1.0/winterforge/plugins/token/__init__.py +11 -0
- winterforge-0.1.0/winterforge/plugins/token/frag_provider.py +113 -0
- winterforge-0.1.0/winterforge/plugins/token/jwt_provider.py +123 -0
- winterforge-0.1.0/winterforge/plugins/token/manager.py +113 -0
- winterforge-0.1.0/winterforge/plugins/validators/__init__.py +13 -0
- winterforge-0.1.0/winterforge/plugins/validators/bootstrap.py +12 -0
- winterforge-0.1.0/winterforge/plugins/validators/email.py +70 -0
- winterforge-0.1.0/winterforge/plugins/validators/manager.py +103 -0
- winterforge-0.1.0/winterforge/plugins/validators/password_validators/__init__.py +19 -0
- winterforge-0.1.0/winterforge/plugins/validators/password_validators/simple.py +38 -0
- winterforge-0.1.0/winterforge/plugins/validators/password_validators/strong.py +61 -0
- winterforge-0.1.0/winterforge/plugins/validators/required.py +37 -0
- winterforge-0.1.0/winterforge/plugins/validators/username.py +192 -0
- winterforge-0.1.0/winterforge/protocols/__init__.py +26 -0
- winterforge-0.1.0/winterforge/protocols/collections.py +51 -0
- winterforge-0.1.0/winterforge/protocols/resolvable.py +72 -0
- winterforge-0.1.0/winterforge/testing/__init__.py +9 -0
- winterforge-0.1.0/winterforge/testing/cli.py +325 -0
- winterforge-0.1.0/winterforge/utils/__init__.py +5 -0
- winterforge-0.1.0/winterforge/utils/dependency_resolver.py +164 -0
- winterforge-0.1.0/winterforge/utils/naming.py +137 -0
- winterforge-0.1.0/winterforge/utils/signature.py +122 -0
- winterforge-0.1.0/winterforge/utils/sync.py +86 -0
- winterforge-0.1.0/winterforge.egg-info/PKG-INFO +19 -0
- winterforge-0.1.0/winterforge.egg-info/SOURCES.txt +390 -0
- winterforge-0.1.0/winterforge.egg-info/dependency_links.txt +1 -0
- winterforge-0.1.0/winterforge.egg-info/entry_points.txt +36 -0
- winterforge-0.1.0/winterforge.egg-info/requires.txt +14 -0
- winterforge-0.1.0/winterforge.egg-info/top_level.txt +3 -0
- winterforge-0.1.0/winterforge_channels/__init__.py +18 -0
- winterforge-0.1.0/winterforge_channels/cli/__init__.py +5 -0
- winterforge-0.1.0/winterforge_channels/cli/channel_commands.py +175 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/__init__.py +19 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/conversable.py +59 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/messageable.py +75 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/routable.py +54 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/subscribable.py +90 -0
- winterforge-0.1.0/winterforge_channels/frags/traits/transportable.py +57 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/__init__.py +38 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/http_egress.py +163 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/http_ingress.py +122 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/managers.py +145 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/protocols.py +127 -0
- winterforge-0.1.0/winterforge_channels/plugins/transports/websocket_egress.py +202 -0
- winterforge-0.1.0/winterforge_channels/primitives/__init__.py +11 -0
- winterforge-0.1.0/winterforge_channels/primitives/channel.py +164 -0
- winterforge-0.1.0/winterforge_channels/primitives/message.py +162 -0
- winterforge-0.1.0/winterforge_channels/primitives/subscription.py +73 -0
- winterforge-0.1.0/winterforge_channels/py.typed +1 -0
- winterforge-0.1.0/winterforge_channels/registries/__init__.py +17 -0
- winterforge-0.1.0/winterforge_channels/registries/channel_registry.py +98 -0
- winterforge-0.1.0/winterforge_channels/registries/message_registry.py +138 -0
- winterforge-0.1.0/winterforge_channels/registries/subscription_registry.py +104 -0
- winterforge-0.1.0/winterforge_dx_tools/__init__.py +17 -0
- winterforge-0.1.0/winterforge_dx_tools/builder.py +154 -0
- winterforge-0.1.0/winterforge_dx_tools/cli/__init__.py +4 -0
- winterforge-0.1.0/winterforge_dx_tools/cli/commands.py +350 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/__init__.py +15 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/builder_panel.py +74 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/builder_progress.py +81 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/builder_table.py +100 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/builder_tree.py +102 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/manager.py +23 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/panel.py +60 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/progress.py +54 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/table.py +76 -0
- winterforge-0.1.0/winterforge_dx_tools/elements/tree.py +79 -0
- winterforge-0.1.0/winterforge_dx_tools/formatters/__init__.py +9 -0
- winterforge-0.1.0/winterforge_dx_tools/formatters/manager.py +24 -0
- winterforge-0.1.0/winterforge_dx_tools/formatters/prettier.py +84 -0
- winterforge-0.1.0/winterforge_dx_tools/generators/__init__.py +1 -0
- winterforge-0.1.0/winterforge_dx_tools/generators/generator.py +132 -0
- winterforge-0.1.0/winterforge_dx_tools/generators/templates.py +54 -0
- winterforge-0.1.0/winterforge_dx_tools/graphql/__init__.py +1 -0
- winterforge-0.1.0/winterforge_dx_tools/graphql/resolver_factory.py +97 -0
- winterforge-0.1.0/winterforge_dx_tools/graphql/schema_generator.py +111 -0
- winterforge-0.1.0/winterforge_dx_tools/graphql/server.py +108 -0
- winterforge-0.1.0/winterforge_dx_tools/graphql/type_mapper.py +108 -0
- winterforge-0.1.0/winterforge_dx_tools/migrations/__init__.py +6 -0
- winterforge-0.1.0/winterforge_dx_tools/migrations/executor.py +189 -0
- winterforge-0.1.0/winterforge_dx_tools/migrations/generator.py +112 -0
- winterforge-0.1.0/winterforge_dx_tools/migrations/template.py +31 -0
- winterforge-0.1.0/winterforge_dx_tools/migrations/tracker.py +132 -0
- winterforge-0.1.0/winterforge_dx_tools/openapi/__init__.py +13 -0
- winterforge-0.1.0/winterforge_dx_tools/openapi/generator.py +168 -0
- winterforge-0.1.0/winterforge_dx_tools/openapi/schemas.py +135 -0
- winterforge-0.1.0/winterforge_dx_tools/server/__init__.py +7 -0
- winterforge-0.1.0/winterforge_dx_tools/server/app.py +78 -0
- winterforge-0.1.0/winterforge_dx_tools/server/endpoint_generator.py +144 -0
- winterforge-0.1.0/winterforge_dx_tools/server/request_handler.py +99 -0
- winterforge-0.1.0/winterforge_dx_tools/server/response_handler.py +89 -0
- winterforge-0.1.0/winterforge_dx_tools/testing/__init__.py +32 -0
- winterforge-0.1.0/winterforge_dx_tools/testing/assertions.py +169 -0
- winterforge-0.1.0/winterforge_dx_tools/testing/builders.py +221 -0
- winterforge-0.1.0/winterforge_dx_tools/testing/factories.py +189 -0
- winterforge-0.1.0/winterforge_dx_tools/utils/__init__.py +6 -0
- winterforge-0.1.0/winterforge_dx_tools/utils/http_method_detector.py +107 -0
- winterforge-0.1.0/winterforge_dx_tools/utils/introspection.py +80 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: winterforge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI-native framework with composable primitives
|
|
5
|
+
Author: Beau Simensen
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
8
|
+
Requires-Dist: asyncpg>=0.29.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0.0
|
|
10
|
+
Requires-Dist: pyyaml>=6.0
|
|
11
|
+
Requires-Dist: click>=8.1.7
|
|
12
|
+
Requires-Dist: argon2-cffi>=23.1.0
|
|
13
|
+
Requires-Dist: bcrypt>=4.1.2
|
|
14
|
+
Requires-Dist: PyJWT>=2.8.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
19
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# WinterForge
|
|
2
|
+
|
|
3
|
+
**Async-first framework for composable data primitives.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from winterforge.frags import Frag
|
|
11
|
+
from winterforge.frags.registries import UserRegistry
|
|
12
|
+
from winterforge.plugins import discover_plugins, StorageManager
|
|
13
|
+
|
|
14
|
+
# Initialize
|
|
15
|
+
discover_plugins()
|
|
16
|
+
StorageManager.load_bootstrap()
|
|
17
|
+
|
|
18
|
+
# Create user
|
|
19
|
+
users = UserRegistry()
|
|
20
|
+
user = await users.create(
|
|
21
|
+
username='alice',
|
|
22
|
+
email='alice@example.com',
|
|
23
|
+
password='secure123'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Query
|
|
27
|
+
user = await users.get('alice@example.com') # Email resolution
|
|
28
|
+
active = await users.verified_only().all() # Filter
|
|
29
|
+
|
|
30
|
+
# Create Frag
|
|
31
|
+
post = Frag(
|
|
32
|
+
affinities=['post', 'published'],
|
|
33
|
+
traits=['titled', 'timestamped', 'sluggable']
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
post.set_title("Hello World") \
|
|
37
|
+
.generate_slug("Hello World")
|
|
38
|
+
|
|
39
|
+
await post.save()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Core Concepts
|
|
45
|
+
|
|
46
|
+
### Frags
|
|
47
|
+
Composable data primitives built from:
|
|
48
|
+
- **Affinities** - What it IS (tags: `['user', 'admin']`)
|
|
49
|
+
- **Traits** - What it CAN DO (behavior: `['titled', 'persistable']`)
|
|
50
|
+
- **Aliases** - Flexible metadata (relations: `{'employee_id': '12345'}`)
|
|
51
|
+
|
|
52
|
+
### Registries
|
|
53
|
+
Query collections of Frags by composition.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
users = UserRegistry()
|
|
57
|
+
admins = await users.with_role('admin').all()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Storage
|
|
61
|
+
**SQLite**, **PostgreSQL**, **YAML**, **Env** backends.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
storage = StorageManager.get('sqlite', db_path='app.db')
|
|
65
|
+
await storage.save(frag)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Testing
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# All tests
|
|
82
|
+
pytest
|
|
83
|
+
|
|
84
|
+
# Core tests (no PostgreSQL)
|
|
85
|
+
pytest --ignore=tests/frags/storage/test_postgresql_integration.py
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Expected:** 581 passing, 1 warning (JWT dev secret)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Documentation
|
|
93
|
+
|
|
94
|
+
- **Quick Start:** `QUICKSTART.md` - 5-minute introduction
|
|
95
|
+
- **Glossary:** `GLOSSARY.md` - Complete terminology reference
|
|
96
|
+
- **Handoff:** `HANDOFF.md` - Recent changes and architecture
|
|
97
|
+
- **Examples:** `examples/pkm/` - Personal knowledge management app
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Project Structure
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
winterforge/
|
|
105
|
+
├── frags/ # Frag core
|
|
106
|
+
│ ├── base.py # Frag class
|
|
107
|
+
│ ├── registry.py # FragRegistry
|
|
108
|
+
│ ├── traits/ # Built-in traits
|
|
109
|
+
│ └── registries/ # Specialized registries
|
|
110
|
+
├── plugins/ # Plugin system
|
|
111
|
+
│ ├── storage/ # Storage backends
|
|
112
|
+
│ ├── identity/ # Identity resolvers
|
|
113
|
+
│ └── managers/ # Plugin managers
|
|
114
|
+
└── cli/ # CLI commands
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Built-in Traits
|
|
120
|
+
|
|
121
|
+
- **titled** - title, subtitle
|
|
122
|
+
- **sluggable** - URL slugs
|
|
123
|
+
- **timestamped** - created_on, updated_on
|
|
124
|
+
- **persistable** - save(), delete(), is_new()
|
|
125
|
+
- **userable** - username, email, email_verified
|
|
126
|
+
- **authenticatable** - password hashing
|
|
127
|
+
- **authorizable** - roles, permissions
|
|
128
|
+
- **sessionable** - session management
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## CLI
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# User management
|
|
136
|
+
winterforge user create alice alice@example.com
|
|
137
|
+
winterforge user add-role alice admin
|
|
138
|
+
|
|
139
|
+
# Session management
|
|
140
|
+
winterforge session create alice
|
|
141
|
+
winterforge session verify <token>
|
|
142
|
+
|
|
143
|
+
# Role/permission management
|
|
144
|
+
winterforge role create admin "Admin role"
|
|
145
|
+
winterforge permission create posts.create
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Async-First
|
|
151
|
+
|
|
152
|
+
**All I/O operations are async:**
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# Storage
|
|
156
|
+
await storage.save(frag)
|
|
157
|
+
frag = await storage.load(id)
|
|
158
|
+
|
|
159
|
+
# Frags
|
|
160
|
+
await frag.save()
|
|
161
|
+
await frag.delete()
|
|
162
|
+
|
|
163
|
+
# Registry
|
|
164
|
+
frag = await registry.get(identity)
|
|
165
|
+
all_frags = await registry.all()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Sync wrapper (rare):**
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from winterforge.utils.sync import to_sync
|
|
172
|
+
|
|
173
|
+
@to_sync
|
|
174
|
+
async def create_user_sync(username, email):
|
|
175
|
+
users = UserRegistry()
|
|
176
|
+
return await users.create(username, email, 'password')
|
|
177
|
+
|
|
178
|
+
user = create_user_sync('alice', 'alice@example.com')
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Architecture
|
|
184
|
+
|
|
185
|
+
- **Composition over inheritance** - Frags compose capabilities
|
|
186
|
+
- **Plugin-based** - Extensible via decorators
|
|
187
|
+
- **Async-first** - Modern Python standards
|
|
188
|
+
- **Everything is a Frag** - Even CLI output
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Status
|
|
193
|
+
|
|
194
|
+
**Version:** Pre-release (Phase 4 complete)
|
|
195
|
+
**Tests:** 581 passing
|
|
196
|
+
**Warnings:** 1 (expected)
|
|
197
|
+
**Ready for:** Feature development
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
**WinterForge is async-first, composition-driven, and ready for production.**
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "winterforge"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "AI-native framework with composable primitives"
|
|
5
|
+
authors = [{name = "Beau Simensen"}]
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typing-extensions>=4.0.0",
|
|
9
|
+
"asyncpg>=0.29.0",
|
|
10
|
+
"pydantic>=2.0.0",
|
|
11
|
+
"pyyaml>=6.0",
|
|
12
|
+
"click>=8.1.7",
|
|
13
|
+
"argon2-cffi>=23.1.0",
|
|
14
|
+
"bcrypt>=4.1.2",
|
|
15
|
+
"PyJWT>=2.8.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=7.0.0",
|
|
21
|
+
"pytest-cov>=4.0.0",
|
|
22
|
+
"pytest-asyncio>=0.21.0",
|
|
23
|
+
"mypy>=1.0.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.entry-points."winterforge.storage_backends"]
|
|
27
|
+
yaml = "winterforge.plugins.storage.yaml:YamlStorageBackend"
|
|
28
|
+
sqlite = "winterforge.plugins.storage.sqlite:SQLiteStorageBackend"
|
|
29
|
+
postgresql = "winterforge.plugins.storage.postgresql:PostgreSQLStorageBackend"
|
|
30
|
+
|
|
31
|
+
[project.entry-points."winterforge.identity_resolvers"]
|
|
32
|
+
frag_id = "winterforge.plugins.identity.frag_id_resolver:FragIdResolver"
|
|
33
|
+
slug = "winterforge.plugins.identity.slug_resolver:SlugResolver"
|
|
34
|
+
title = "winterforge.plugins.identity.title_resolver:TitleIdentityResolver"
|
|
35
|
+
uuid = "winterforge.plugins.identity.uuid_resolver:UuidResolver"
|
|
36
|
+
email = "winterforge.plugins.identity.email_resolver:EmailIdentityResolver"
|
|
37
|
+
username = "winterforge.plugins.identity.username_resolver:UsernameIdentityResolver"
|
|
38
|
+
|
|
39
|
+
[project.entry-points."winterforge.frag_traits"]
|
|
40
|
+
fieldable = "winterforge.frags.traits.fieldable:FieldableTrait"
|
|
41
|
+
titled = "winterforge.frags.traits.titled:TitledTrait"
|
|
42
|
+
typed = "winterforge.frags.traits.typed:TypedTrait"
|
|
43
|
+
timestamped = "winterforge.frags.traits.timestamped:TimestampedTrait"
|
|
44
|
+
sluggable = "winterforge.frags.traits.sluggable:SluggableTrait"
|
|
45
|
+
persistable = "winterforge.frags.traits.persistable:PersistableTrait"
|
|
46
|
+
userable = "winterforge.frags.traits.userable:UserableTrait"
|
|
47
|
+
authenticatable = "winterforge.frags.traits.authenticatable:AuthenticatableTrait"
|
|
48
|
+
authorizable = "winterforge.frags.traits.authorizable:AuthorizableTrait"
|
|
49
|
+
sessionable = "winterforge.frags.traits.sessionable:SessionableTrait"
|
|
50
|
+
tokenable = "winterforge.frags.traits.tokenable:TokenableTrait"
|
|
51
|
+
permissioned = "winterforge.frags.traits.permissioned:PermissionedTrait"
|
|
52
|
+
ownable = "winterforge.frags.traits.ownable:OwnableTrait"
|
|
53
|
+
|
|
54
|
+
[project.entry-points."winterforge.dx.elements"]
|
|
55
|
+
panel = "winterforge_dx_tools.elements.builder_panel:PanelElement"
|
|
56
|
+
table = "winterforge_dx_tools.elements.builder_table:TableElement"
|
|
57
|
+
progress = "winterforge_dx_tools.elements.builder_progress:ProgressElement"
|
|
58
|
+
tree = "winterforge_dx_tools.elements.builder_tree:TreeElement"
|
|
59
|
+
|
|
60
|
+
[project.scripts]
|
|
61
|
+
winterforge = "winterforge.cli:cli"
|
|
62
|
+
|
|
63
|
+
[build-system]
|
|
64
|
+
requires = ["setuptools>=65.0"]
|
|
65
|
+
build-backend = "setuptools.build_meta"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["."]
|
|
69
|
+
include = ["winterforge*"]
|
|
70
|
+
exclude = ["tests*", "mandates*", "winterforge_data*"]
|
|
71
|
+
|
|
72
|
+
[tool.pytest.ini_options]
|
|
73
|
+
testpaths = ["tests"]
|
|
74
|
+
python_files = ["test_*.py"]
|
|
75
|
+
python_classes = ["Test*"]
|
|
76
|
+
python_functions = ["test_*"]
|
|
77
|
+
asyncio_mode = "auto"
|
|
78
|
+
|
|
79
|
+
[tool.mypy]
|
|
80
|
+
python_version = "3.10"
|
|
81
|
+
warn_return_any = true
|
|
82
|
+
warn_unused_configs = true
|
|
83
|
+
disallow_untyped_defs = true
|
|
84
|
+
|
|
85
|
+
[tool.coverage.run]
|
|
86
|
+
source = ["winterforge"]
|
|
87
|
+
omit = ["tests/*"]
|
|
88
|
+
|
|
89
|
+
[tool.coverage.report]
|
|
90
|
+
exclude_lines = [
|
|
91
|
+
"pragma: no cover",
|
|
92
|
+
"def __repr__",
|
|
93
|
+
"raise AssertionError",
|
|
94
|
+
"raise NotImplementedError",
|
|
95
|
+
"if __name__ == .__main__.:",
|
|
96
|
+
]
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Acceptance criteria validation tests.
|
|
3
|
+
|
|
4
|
+
These tests validate that all acceptance criteria from the spec are met.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from winterforge.plugins import (
|
|
9
|
+
storage_backend,
|
|
10
|
+
frag_trait,
|
|
11
|
+
deriver,
|
|
12
|
+
StorageManager,
|
|
13
|
+
FragTraitManager,
|
|
14
|
+
root,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestAcceptanceCriteria:
|
|
19
|
+
"""Validate acceptance criteria from specification."""
|
|
20
|
+
|
|
21
|
+
def test_public_api_decorator_registration(self) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Acceptance: Public API works - decorator registration.
|
|
24
|
+
|
|
25
|
+
from winterforge.plugins import storage_backend, StorageManager
|
|
26
|
+
|
|
27
|
+
@storage_backend('test')
|
|
28
|
+
class TestBackend:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
backend = StorageManager.get('test')
|
|
32
|
+
assert backend is not None
|
|
33
|
+
"""
|
|
34
|
+
StorageManager.reset()
|
|
35
|
+
|
|
36
|
+
@storage_backend()
|
|
37
|
+
@root('test-ac-1')
|
|
38
|
+
class TestBackend:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
backend = StorageManager.get('test-ac-1')
|
|
42
|
+
assert backend is not None
|
|
43
|
+
assert isinstance(backend, TestBackend)
|
|
44
|
+
|
|
45
|
+
def test_public_api_import_structure(self) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Acceptance: All components importable from top-level.
|
|
48
|
+
"""
|
|
49
|
+
# Should be able to import everything from winterforge.plugins
|
|
50
|
+
from winterforge.plugins import (
|
|
51
|
+
PluginManagerBase,
|
|
52
|
+
plugin,
|
|
53
|
+
deriver,
|
|
54
|
+
storage_backend,
|
|
55
|
+
frag_trait,
|
|
56
|
+
identity_resolver,
|
|
57
|
+
StorageManager,
|
|
58
|
+
FragTraitManager,
|
|
59
|
+
IdentityResolverManager,
|
|
60
|
+
StorageBackend,
|
|
61
|
+
ReadOnlyStorage,
|
|
62
|
+
FragTrait,
|
|
63
|
+
IdentityResolverProvider,
|
|
64
|
+
discover_plugins,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# All imports successful
|
|
68
|
+
assert PluginManagerBase is not None
|
|
69
|
+
assert plugin is not None
|
|
70
|
+
assert deriver is not None
|
|
71
|
+
assert StorageManager is not None
|
|
72
|
+
assert ReadOnlyStorage is not None
|
|
73
|
+
|
|
74
|
+
def test_plugin_derivatives_work(self) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Acceptance: Plugin derivatives work.
|
|
77
|
+
|
|
78
|
+
from winterforge.plugins import deriver, StorageManager
|
|
79
|
+
|
|
80
|
+
@deriver('winterforge.storage_backends')
|
|
81
|
+
def test_deriver():
|
|
82
|
+
yield {'id': 'derived', 'class': object}
|
|
83
|
+
|
|
84
|
+
StorageManager.derive_plugins()
|
|
85
|
+
assert StorageManager.has('derived')
|
|
86
|
+
"""
|
|
87
|
+
StorageManager.reset()
|
|
88
|
+
|
|
89
|
+
class DerivedClass:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
@deriver('winterforge.storage_backends')
|
|
93
|
+
def test_deriver_ac():
|
|
94
|
+
yield {'id': 'derived_ac', 'class': DerivedClass}
|
|
95
|
+
|
|
96
|
+
StorageManager.derive_plugins()
|
|
97
|
+
assert StorageManager.has('derived_ac')
|
|
98
|
+
|
|
99
|
+
instance = StorageManager.get('derived_ac')
|
|
100
|
+
assert isinstance(instance, DerivedClass)
|
|
101
|
+
|
|
102
|
+
def test_lazy_instantiation_with_caching(self) -> None:
|
|
103
|
+
"""Acceptance: Lazy instantiation with caching works."""
|
|
104
|
+
StorageManager.reset()
|
|
105
|
+
|
|
106
|
+
instantiation_count = 0
|
|
107
|
+
|
|
108
|
+
@storage_backend()
|
|
109
|
+
@root('cached')
|
|
110
|
+
class CachedBackend:
|
|
111
|
+
def __init__(self):
|
|
112
|
+
nonlocal instantiation_count
|
|
113
|
+
instantiation_count += 1
|
|
114
|
+
|
|
115
|
+
# Not instantiated yet
|
|
116
|
+
assert instantiation_count == 0
|
|
117
|
+
|
|
118
|
+
# First access
|
|
119
|
+
instance1 = StorageManager.get('cached')
|
|
120
|
+
assert instantiation_count == 1
|
|
121
|
+
|
|
122
|
+
# Second access - cached
|
|
123
|
+
instance2 = StorageManager.get('cached')
|
|
124
|
+
assert instantiation_count == 1
|
|
125
|
+
assert instance1 is instance2
|
|
126
|
+
|
|
127
|
+
def test_metadata_support(self) -> None:
|
|
128
|
+
"""Acceptance: Plugin metadata works."""
|
|
129
|
+
StorageManager.reset()
|
|
130
|
+
|
|
131
|
+
@storage_backend(description='Test', version='1.0')
|
|
132
|
+
@root('with-meta')
|
|
133
|
+
class MetaBackend:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
metadata = StorageManager.get_metadata('with-meta')
|
|
137
|
+
assert metadata['description'] == 'Test'
|
|
138
|
+
assert metadata['version'] == '1.0'
|
|
139
|
+
|
|
140
|
+
def test_manager_methods_work(self) -> None:
|
|
141
|
+
"""Acceptance: All manager methods work correctly."""
|
|
142
|
+
StorageManager.reset()
|
|
143
|
+
|
|
144
|
+
@storage_backend()
|
|
145
|
+
@root('method-test')
|
|
146
|
+
class MethodTestBackend:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
# has()
|
|
150
|
+
assert StorageManager.has('method-test')
|
|
151
|
+
|
|
152
|
+
# get()
|
|
153
|
+
backend = StorageManager.get('method-test')
|
|
154
|
+
assert backend is not None
|
|
155
|
+
|
|
156
|
+
# get_definitions()
|
|
157
|
+
defs = StorageManager.get_definitions()
|
|
158
|
+
assert 'method-test' in defs
|
|
159
|
+
|
|
160
|
+
# get_metadata()
|
|
161
|
+
meta = StorageManager.get_metadata('method-test')
|
|
162
|
+
assert isinstance(meta, dict)
|
|
163
|
+
|
|
164
|
+
# clear_cache()
|
|
165
|
+
instance1 = StorageManager.get('method-test')
|
|
166
|
+
StorageManager.clear_cache()
|
|
167
|
+
instance2 = StorageManager.get('method-test')
|
|
168
|
+
assert instance1 is not instance2
|
|
169
|
+
|
|
170
|
+
# reset()
|
|
171
|
+
StorageManager.reset()
|
|
172
|
+
assert not StorageManager.has('method-test')
|
|
173
|
+
|
|
174
|
+
def test_trait_manager_get_trait_fields(self) -> None:
|
|
175
|
+
"""Acceptance: FragTraitManager.get_trait_fields() works."""
|
|
176
|
+
FragTraitManager.reset()
|
|
177
|
+
|
|
178
|
+
class MockField:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
@frag_trait()
|
|
182
|
+
@root('fields-test')
|
|
183
|
+
class FieldsTrait:
|
|
184
|
+
# Use private attributes (new convention)
|
|
185
|
+
_field1 = MockField()
|
|
186
|
+
_field2 = MockField()
|
|
187
|
+
|
|
188
|
+
fields = FragTraitManager.get_trait_fields('fields-test')
|
|
189
|
+
assert '_field1' in fields
|
|
190
|
+
assert '_field2' in fields
|
|
191
|
+
|
|
192
|
+
def test_custom_instantiation_args(self) -> None:
|
|
193
|
+
"""Acceptance: Custom instantiation args work (not cached)."""
|
|
194
|
+
StorageManager.reset()
|
|
195
|
+
|
|
196
|
+
@storage_backend()
|
|
197
|
+
@root('custom-args')
|
|
198
|
+
class CustomArgsBackend:
|
|
199
|
+
def __init__(self, value=None):
|
|
200
|
+
self.value = value
|
|
201
|
+
|
|
202
|
+
instance1 = StorageManager.get('custom-args', value='a')
|
|
203
|
+
instance2 = StorageManager.get('custom-args', value='b')
|
|
204
|
+
|
|
205
|
+
assert instance1.value == 'a'
|
|
206
|
+
assert instance2.value == 'b'
|
|
207
|
+
assert instance1 is not instance2
|
|
208
|
+
|
|
209
|
+
def test_all_plugin_types(self) -> None:
|
|
210
|
+
"""Acceptance: All plugin types work."""
|
|
211
|
+
from winterforge.plugins import (
|
|
212
|
+
storage_backend,
|
|
213
|
+
frag_trait,
|
|
214
|
+
identity_resolver,
|
|
215
|
+
StorageManager,
|
|
216
|
+
FragTraitManager,
|
|
217
|
+
IdentityResolverManager,
|
|
218
|
+
root,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
StorageManager.reset()
|
|
222
|
+
FragTraitManager.reset()
|
|
223
|
+
IdentityResolverManager.reset()
|
|
224
|
+
|
|
225
|
+
@storage_backend()
|
|
226
|
+
@root('storage-test')
|
|
227
|
+
class StorageTest:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
@frag_trait()
|
|
231
|
+
@root('trait-test')
|
|
232
|
+
class TraitTest:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
@identity_resolver()
|
|
236
|
+
@root('resolver-test')
|
|
237
|
+
class ResolverTest:
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
assert StorageManager.has('storage-test')
|
|
241
|
+
assert FragTraitManager.has('trait-test')
|
|
242
|
+
assert IdentityResolverManager.has('resolver-test')
|
|
243
|
+
|
|
244
|
+
def test_error_handling(self) -> None:
|
|
245
|
+
"""Acceptance: Proper error handling."""
|
|
246
|
+
StorageManager.reset()
|
|
247
|
+
|
|
248
|
+
# Duplicate registration is now idempotent (silently skips)
|
|
249
|
+
@storage_backend()
|
|
250
|
+
@root('dup-test')
|
|
251
|
+
class DupTest:
|
|
252
|
+
pass
|
|
253
|
+
|
|
254
|
+
# Second registration should silently succeed (idempotent)
|
|
255
|
+
StorageManager.register('dup-test', DupTest)
|
|
256
|
+
assert StorageManager.has('dup-test')
|
|
257
|
+
|
|
258
|
+
# Plugin not found
|
|
259
|
+
with pytest.raises(KeyError, match="not found"):
|
|
260
|
+
StorageManager.get('nonexistent')
|
|
261
|
+
|
|
262
|
+
# Trait not found
|
|
263
|
+
with pytest.raises(KeyError, match="not found"):
|
|
264
|
+
FragTraitManager.get_trait_fields('nonexistent')
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestIntegrationScenarios:
|
|
268
|
+
"""Test real-world integration scenarios."""
|
|
269
|
+
|
|
270
|
+
def test_complete_workflow(self) -> None:
|
|
271
|
+
"""Test complete plugin workflow from registration to use."""
|
|
272
|
+
StorageManager.reset()
|
|
273
|
+
|
|
274
|
+
# 1. Register a plugin
|
|
275
|
+
@storage_backend(description='Test workflow')
|
|
276
|
+
@root('workflow-test')
|
|
277
|
+
class WorkflowBackend:
|
|
278
|
+
def __init__(self):
|
|
279
|
+
self.saved_items = []
|
|
280
|
+
|
|
281
|
+
def save(self, item):
|
|
282
|
+
self.saved_items.append(item)
|
|
283
|
+
|
|
284
|
+
# 2. Check registration
|
|
285
|
+
assert StorageManager.has('workflow-test')
|
|
286
|
+
|
|
287
|
+
# 3. Get instance
|
|
288
|
+
backend = StorageManager.get('workflow-test')
|
|
289
|
+
|
|
290
|
+
# 4. Use plugin
|
|
291
|
+
backend.save('item1')
|
|
292
|
+
backend.save('item2')
|
|
293
|
+
|
|
294
|
+
# 5. Verify caching
|
|
295
|
+
backend2 = StorageManager.get('workflow-test')
|
|
296
|
+
assert backend is backend2
|
|
297
|
+
assert len(backend2.saved_items) == 2
|
|
298
|
+
|
|
299
|
+
def test_derivative_workflow(self) -> None:
|
|
300
|
+
"""Test complete derivative workflow."""
|
|
301
|
+
StorageManager.reset()
|
|
302
|
+
|
|
303
|
+
# 1. Define base class
|
|
304
|
+
class DatabaseBackend:
|
|
305
|
+
def __init__(self, config=None):
|
|
306
|
+
self.config = config
|
|
307
|
+
|
|
308
|
+
# 2. Register deriver
|
|
309
|
+
@deriver('winterforge.storage_backends')
|
|
310
|
+
def db_deriver():
|
|
311
|
+
configs = {'primary': {'host': 'localhost'}, 'backup': {'host': 'remote'}}
|
|
312
|
+
for name, config in configs.items():
|
|
313
|
+
yield {
|
|
314
|
+
'id': f'db_{name}',
|
|
315
|
+
'class': DatabaseBackend,
|
|
316
|
+
'metadata': {'config': config}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# 3. Derive plugins
|
|
320
|
+
StorageManager.derive_plugins()
|
|
321
|
+
|
|
322
|
+
# 4. Verify derived plugins exist
|
|
323
|
+
assert StorageManager.has('db_primary')
|
|
324
|
+
assert StorageManager.has('db_backup')
|
|
325
|
+
|
|
326
|
+
# 5. Use derived plugins
|
|
327
|
+
primary = StorageManager.get('db_primary')
|
|
328
|
+
backup = StorageManager.get('db_backup')
|
|
329
|
+
|
|
330
|
+
assert isinstance(primary, DatabaseBackend)
|
|
331
|
+
assert isinstance(backup, DatabaseBackend)
|