@shortlink-org/portolan 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/catalog/enum_test.go +46 -0
- package/catalog/model.go +932 -0
- package/catalog/roundtrip_test.go +185 -0
- package/catalog/via_test.go +38 -0
- package/cli/portolan.mjs +368 -0
- package/go.mod +14 -0
- package/go.sum +14 -0
- package/index.html +13 -0
- package/internal/commands/cargo.go +154 -0
- package/internal/commands/commands.go +143 -0
- package/internal/commands/commands_test.go +323 -0
- package/internal/commands/gradle.go +77 -0
- package/internal/commands/justfile.go +82 -0
- package/internal/commands/makefile.go +106 -0
- package/internal/commands/maven.go +93 -0
- package/internal/commands/packagejson.go +159 -0
- package/internal/commands/pyproject.go +169 -0
- package/internal/commands/taskfile.go +101 -0
- package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
- package/internal/commands/testdata/estate/Makefile +33 -0
- package/internal/commands/testdata/estate/Taskfile.yml +21 -0
- package/internal/commands/testdata/estate/build.gradle.kts +21 -0
- package/internal/commands/testdata/estate/justfile +20 -0
- package/internal/commands/testdata/estate/package.json +13 -0
- package/internal/commands/testdata/estate/pom.xml +29 -0
- package/internal/commands/testdata/estate/pyproject.toml +27 -0
- package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
- package/internal/commands/testdata/golden/commands.json +305 -0
- package/internal/gohttp/analyze.go +2500 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +143 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +36 -0
- package/internal/goscan/tree.go +155 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +62 -0
- package/internal/wsdl/parse.go +920 -0
- package/internal/wsdl/parse_test.go +133 -0
- package/package.json +117 -0
- package/plugin/describe.go +107 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +117 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +1160 -0
- package/plugins/extract-adr/describe.go +19 -0
- package/plugins/extract-adr/describe_test.go +11 -0
- package/plugins/extract-adr/extract.go +153 -0
- package/plugins/extract-adr/extract_test.go +350 -0
- package/plugins/extract-adr/history.go +99 -0
- package/plugins/extract-adr/main.go +65 -0
- package/plugins/extract-adr/options.schema.json +26 -0
- package/plugins/extract-adr/parse.go +642 -0
- package/plugins/extract-adr/parse_test.go +405 -0
- package/plugins/extract-asyncapi/describe.go +19 -0
- package/plugins/extract-asyncapi/describe_test.go +11 -0
- package/plugins/extract-asyncapi/extract.go +315 -0
- package/plugins/extract-asyncapi/extract_test.go +197 -0
- package/plugins/extract-asyncapi/main.go +48 -0
- package/plugins/extract-asyncapi/options.schema.json +26 -0
- package/plugins/extract-asyncapi/spec.go +151 -0
- package/plugins/extract-celery/README.md +169 -0
- package/plugins/extract-celery/extract.py +346 -0
- package/plugins/extract-celery/extract_test.py +192 -0
- package/plugins/extract-celery/main.py +66 -0
- package/plugins/extract-celery/options.py +31 -0
- package/plugins/extract-celery/options.schema.json +31 -0
- package/plugins/extract-celery/producers.py +156 -0
- package/plugins/extract-celery/routes_test.py +106 -0
- package/plugins/extract-commands/describe.go +19 -0
- package/plugins/extract-commands/describe_test.go +11 -0
- package/plugins/extract-commands/extract.go +79 -0
- package/plugins/extract-commands/extract_test.go +92 -0
- package/plugins/extract-commands/main.go +55 -0
- package/plugins/extract-commands/options.schema.json +20 -0
- package/plugins/extract-csr/avro.go +258 -0
- package/plugins/extract-csr/describe.go +19 -0
- package/plugins/extract-csr/describe_test.go +11 -0
- package/plugins/extract-csr/extract.go +338 -0
- package/plugins/extract-csr/extract_test.go +374 -0
- package/plugins/extract-csr/jsonschema.go +343 -0
- package/plugins/extract-csr/lock.go +25 -0
- package/plugins/extract-csr/main.go +84 -0
- package/plugins/extract-csr/options.schema.json +43 -0
- package/plugins/extract-csr/subject.go +102 -0
- package/plugins/extract-django/README.md +386 -0
- package/plugins/extract-django/apps.py +100 -0
- package/plugins/extract-django/auth.py +302 -0
- package/plugins/extract-django/auth_test.py +188 -0
- package/plugins/extract-django/clients.py +121 -0
- package/plugins/extract-django/contracts.py +458 -0
- package/plugins/extract-django/database.py +119 -0
- package/plugins/extract-django/domain.py +318 -0
- package/plugins/extract-django/events.py +86 -0
- package/plugins/extract-django/extract.py +601 -0
- package/plugins/extract-django/extract_test.py +397 -0
- package/plugins/extract-django/flows.py +1278 -0
- package/plugins/extract-django/flows_test.py +338 -0
- package/plugins/extract-django/ids.py +42 -0
- package/plugins/extract-django/ids_test.py +30 -0
- package/plugins/extract-django/lifecycle.py +222 -0
- package/plugins/extract-django/lifecycle_test.py +76 -0
- package/plugins/extract-django/main.py +66 -0
- package/plugins/extract-django/openapi.py +229 -0
- package/plugins/extract-django/openapi_test.py +60 -0
- package/plugins/extract-django/operations.py +96 -0
- package/plugins/extract-django/options.py +61 -0
- package/plugins/extract-django/options.schema.json +126 -0
- package/plugins/extract-django/routing.py +214 -0
- package/plugins/extract-django/routing_test.py +198 -0
- package/plugins/extract-django/serializers.py +558 -0
- package/plugins/extract-django/store.py +434 -0
- package/plugins/extract-django/transport.py +242 -0
- package/plugins/extract-flows/describe.go +19 -0
- package/plugins/extract-flows/describe_test.go +11 -0
- package/plugins/extract-flows/extract.go +91 -0
- package/plugins/extract-flows/main.go +45 -0
- package/plugins/extract-flows/options.schema.json +16 -0
- package/plugins/extract-flows/parse.go +593 -0
- package/plugins/extract-flows/parse_test.go +204 -0
- package/plugins/extract-glossary/describe.go +19 -0
- package/plugins/extract-glossary/describe_test.go +11 -0
- package/plugins/extract-glossary/extract.go +115 -0
- package/plugins/extract-glossary/extract_test.go +220 -0
- package/plugins/extract-glossary/main.go +59 -0
- package/plugins/extract-glossary/options.schema.json +21 -0
- package/plugins/extract-glossary/parse.go +214 -0
- package/plugins/extract-glossary/parse_test.go +203 -0
- package/plugins/extract-go/aggregate.go +214 -0
- package/plugins/extract-go/client.go +409 -0
- package/plugins/extract-go/client_test.go +266 -0
- package/plugins/extract-go/describe.go +19 -0
- package/plugins/extract-go/describe_test.go +11 -0
- package/plugins/extract-go/enum.go +195 -0
- package/plugins/extract-go/enum_test.go +82 -0
- package/plugins/extract-go/event.go +99 -0
- package/plugins/extract-go/extract.go +191 -0
- package/plugins/extract-go/extract_test.go +261 -0
- package/plugins/extract-go/flow.go +1441 -0
- package/plugins/extract-go/flow_test.go +609 -0
- package/plugins/extract-go/httpclient.go +174 -0
- package/plugins/extract-go/httpclient_test.go +296 -0
- package/plugins/extract-go/ids.go +92 -0
- package/plugins/extract-go/layout.go +225 -0
- package/plugins/extract-go/layout_test.go +125 -0
- package/plugins/extract-go/lifecycle.go +324 -0
- package/plugins/extract-go/lifecycle_test.go +108 -0
- package/plugins/extract-go/main.go +79 -0
- package/plugins/extract-go/operation.go +157 -0
- package/plugins/extract-go/options.schema.json +66 -0
- package/plugins/extract-go/source.go +316 -0
- package/plugins/extract-go/transport.go +321 -0
- package/plugins/extract-go/transport_test.go +145 -0
- package/plugins/extract-go/wiring.go +434 -0
- package/plugins/extract-go-nats/describe.go +19 -0
- package/plugins/extract-go-nats/describe_test.go +11 -0
- package/plugins/extract-go-nats/extract.go +177 -0
- package/plugins/extract-go-nats/extract_test.go +389 -0
- package/plugins/extract-go-nats/index.go +379 -0
- package/plugins/extract-go-nats/main.go +42 -0
- package/plugins/extract-go-nats/options.schema.json +21 -0
- package/plugins/extract-go-nats/resolve.go +161 -0
- package/plugins/extract-go-nats/sites.go +224 -0
- package/plugins/extract-graphql/describe.go +19 -0
- package/plugins/extract-graphql/describe_test.go +11 -0
- package/plugins/extract-graphql/extract.go +433 -0
- package/plugins/extract-graphql/extract_test.go +256 -0
- package/plugins/extract-graphql/ids.go +49 -0
- package/plugins/extract-graphql/lex.go +237 -0
- package/plugins/extract-graphql/main.go +51 -0
- package/plugins/extract-graphql/options.schema.json +31 -0
- package/plugins/extract-graphql/parse.go +621 -0
- package/plugins/extract-graphql/parse_test.go +122 -0
- package/plugins/extract-http-clients/describe.go +19 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +705 -0
- package/plugins/extract-http-clients/extract_test.go +1263 -0
- package/plugins/extract-http-clients/main.go +42 -0
- package/plugins/extract-http-clients/options.schema.json +33 -0
- package/plugins/extract-java/README.md +190 -0
- package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
- package/plugins/extract-java/options.schema.json +70 -0
- package/plugins/extract-openapi/describe.go +19 -0
- package/plugins/extract-openapi/describe_test.go +11 -0
- package/plugins/extract-openapi/discover.go +243 -0
- package/plugins/extract-openapi/extract.go +526 -0
- package/plugins/extract-openapi/extract_test.go +545 -0
- package/plugins/extract-openapi/main.go +75 -0
- package/plugins/extract-openapi/options.schema.json +62 -0
- package/plugins/extract-openapi/spec.go +350 -0
- package/plugins/extract-project/README.md +22 -0
- package/plugins/extract-project/describe.go +19 -0
- package/plugins/extract-project/describe_test.go +11 -0
- package/plugins/extract-project/extract.go +221 -0
- package/plugins/extract-project/extract_test.go +109 -0
- package/plugins/extract-project/main.go +41 -0
- package/plugins/extract-project/options.schema.json +48 -0
- package/plugins/extract-proto/ast.go +125 -0
- package/plugins/extract-proto/consumes.go +77 -0
- package/plugins/extract-proto/describe.go +19 -0
- package/plugins/extract-proto/extract.go +293 -0
- package/plugins/extract-proto/extract_test.go +459 -0
- package/plugins/extract-proto/ids.go +89 -0
- package/plugins/extract-proto/ids_test.go +57 -0
- package/plugins/extract-proto/lex.go +285 -0
- package/plugins/extract-proto/main.go +100 -0
- package/plugins/extract-proto/module.go +120 -0
- package/plugins/extract-proto/options.schema.json +51 -0
- package/plugins/extract-proto/parse.go +720 -0
- package/plugins/extract-proto/parse_test.go +307 -0
- package/plugins/extract-proto/provides.go +236 -0
- package/plugins/extract-proto/resolve.go +222 -0
- package/plugins/extract-proto/resolve_test.go +119 -0
- package/plugins/extract-python-kafka/README.md +36 -0
- package/plugins/extract-python-kafka/extract.py +211 -0
- package/plugins/extract-python-kafka/extract_test.py +68 -0
- package/plugins/extract-python-kafka/main.py +59 -0
- package/plugins/extract-python-kafka/options.py +29 -0
- package/plugins/extract-python-kafka/options.schema.json +31 -0
- package/plugins/extract-redis/README.md +11 -0
- package/plugins/extract-redis/describe.go +19 -0
- package/plugins/extract-redis/describe_test.go +11 -0
- package/plugins/extract-redis/extract.go +183 -0
- package/plugins/extract-redis/extract_test.go +168 -0
- package/plugins/extract-redis/keyspaces.go +469 -0
- package/plugins/extract-redis/main.go +44 -0
- package/plugins/extract-redis/options.schema.json +30 -0
- package/plugins/extract-river/describe.go +19 -0
- package/plugins/extract-river/describe_test.go +11 -0
- package/plugins/extract-river/extract.go +507 -0
- package/plugins/extract-river/extract_test.go +132 -0
- package/plugins/extract-river/main.go +42 -0
- package/plugins/extract-river/options.schema.json +21 -0
- package/plugins/extract-rust/Cargo.lock +133 -0
- package/plugins/extract-rust/Cargo.toml +23 -0
- package/plugins/extract-rust/README.md +204 -0
- package/plugins/extract-rust/options.schema.json +65 -0
- package/plugins/extract-rust/rustfmt.toml +1 -0
- package/plugins/extract-sql/ddl.go +893 -0
- package/plugins/extract-sql/ddl_test.go +401 -0
- package/plugins/extract-sql/describe.go +19 -0
- package/plugins/extract-sql/describe_test.go +11 -0
- package/plugins/extract-sql/layout.go +221 -0
- package/plugins/extract-sql/layout_test.go +55 -0
- package/plugins/extract-sql/lineage.go +107 -0
- package/plugins/extract-sql/main.go +141 -0
- package/plugins/extract-sql/maps.go +564 -0
- package/plugins/extract-sql/maps_java.go +117 -0
- package/plugins/extract-sql/maps_rust.go +333 -0
- package/plugins/extract-sql/maps_rust_test.go +70 -0
- package/plugins/extract-sql/maps_test.go +204 -0
- package/plugins/extract-sql/maps_ts.go +398 -0
- package/plugins/extract-sql/maps_ts_test.go +136 -0
- package/plugins/extract-sql/options.schema.json +53 -0
- package/plugins/extract-sql/projection.go +70 -0
- package/plugins/extract-sql/projection_test.go +50 -0
- package/plugins/extract-sql/store.go +420 -0
- package/plugins/extract-sql/store_test.go +233 -0
- package/plugins/extract-sql/view.go +295 -0
- package/plugins/extract-ts/README.md +306 -0
- package/plugins/extract-ts/ast.test.ts +178 -0
- package/plugins/extract-ts/ast.ts +648 -0
- package/plugins/extract-ts/clients.ts +185 -0
- package/plugins/extract-ts/domain.ts +164 -0
- package/plugins/extract-ts/extract.test.ts +208 -0
- package/plugins/extract-ts/extract.ts +200 -0
- package/plugins/extract-ts/flows.ts +783 -0
- package/plugins/extract-ts/graphql.test.ts +73 -0
- package/plugins/extract-ts/graphql.ts +181 -0
- package/plugins/extract-ts/ids.test.ts +21 -0
- package/plugins/extract-ts/ids.ts +52 -0
- package/plugins/extract-ts/lifecycle.ts +138 -0
- package/plugins/extract-ts/main.ts +48 -0
- package/plugins/extract-ts/openapi.test.ts +64 -0
- package/plugins/extract-ts/openapi.ts +99 -0
- package/plugins/extract-ts/operations.ts +93 -0
- package/plugins/extract-ts/options.schema.json +96 -0
- package/plugins/extract-ts/source.test.ts +174 -0
- package/plugins/extract-ts/source.ts +465 -0
- package/plugins/extract-ts/transport.ts +147 -0
- package/plugins/extract-ts/tsconfig.json +13 -0
- package/plugins/extract-ts/wiring.ts +190 -0
- package/plugins/extract-watermill/describe.go +19 -0
- package/plugins/extract-watermill/describe_test.go +11 -0
- package/plugins/extract-watermill/extract.go +1228 -0
- package/plugins/extract-watermill/extract_test.go +234 -0
- package/plugins/extract-watermill/main.go +41 -0
- package/plugins/extract-watermill/options.schema.json +21 -0
- package/plugins/extract-wsdl/describe.go +19 -0
- package/plugins/extract-wsdl/describe_test.go +11 -0
- package/plugins/extract-wsdl/extract.go +148 -0
- package/plugins/extract-wsdl/extract_test.go +45 -0
- package/plugins/extract-wsdl/main.go +53 -0
- package/plugins/extract-wsdl/options.schema.json +59 -0
- package/plugins/fetch-bsr/auth.go +114 -0
- package/plugins/fetch-bsr/bsr.go +240 -0
- package/plugins/fetch-bsr/cache.go +66 -0
- package/plugins/fetch-bsr/describe.go +19 -0
- package/plugins/fetch-bsr/fetch.go +192 -0
- package/plugins/fetch-bsr/fetch_test.go +465 -0
- package/plugins/fetch-bsr/lock.go +71 -0
- package/plugins/fetch-bsr/main.go +117 -0
- package/plugins/fetch-bsr/options.schema.json +42 -0
- package/plugins/fetch-csr/auth.go +95 -0
- package/plugins/fetch-csr/cache.go +69 -0
- package/plugins/fetch-csr/describe.go +19 -0
- package/plugins/fetch-csr/describe_test.go +11 -0
- package/plugins/fetch-csr/fetch.go +254 -0
- package/plugins/fetch-csr/fetch_test.go +494 -0
- package/plugins/fetch-csr/lock.go +95 -0
- package/plugins/fetch-csr/main.go +122 -0
- package/plugins/fetch-csr/options.schema.json +42 -0
- package/plugins/fetch-csr/registry.go +209 -0
- package/plugins/fetch-git/cache.go +65 -0
- package/plugins/fetch-git/describe.go +19 -0
- package/plugins/fetch-git/describe_test.go +11 -0
- package/plugins/fetch-git/fetch.go +181 -0
- package/plugins/fetch-git/fetch_test.go +332 -0
- package/plugins/fetch-git/git.go +169 -0
- package/plugins/fetch-git/lock.go +72 -0
- package/plugins/fetch-git/main.go +127 -0
- package/plugins/fetch-git/offline.go +39 -0
- package/plugins/fetch-git/options.schema.json +42 -0
- package/plugins/fetch-git/pin.go +90 -0
- package/plugins/fetch-git/pin_test.go +74 -0
- package/plugins/gen-backstage/describe.go +17 -0
- package/plugins/gen-backstage/main.go +22 -0
- package/plugins/gen-backstage/options.schema.json +30 -0
- package/plugins/gen-backstage/plugin.go +473 -0
- package/plugins/gen-backstage/plugin_test.go +145 -0
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +162 -0
- package/plugins/gen-markdown/aggregate.go +362 -0
- package/plugins/gen-markdown/canonical.go +93 -0
- package/plugins/gen-markdown/context.go +90 -0
- package/plugins/gen-markdown/coverage_test.go +89 -0
- package/plugins/gen-markdown/describe.go +19 -0
- package/plugins/gen-markdown/describe_test.go +11 -0
- package/plugins/gen-markdown/external.go +71 -0
- package/plugins/gen-markdown/flow.go +277 -0
- package/plugins/gen-markdown/glossary.go +76 -0
- package/plugins/gen-markdown/glossary_test.go +148 -0
- package/plugins/gen-markdown/llms.go +302 -0
- package/plugins/gen-markdown/main.go +22 -0
- package/plugins/gen-markdown/markdown.go +254 -0
- package/plugins/gen-markdown/markdown_test.go +100 -0
- package/plugins/gen-markdown/module.go +107 -0
- package/plugins/gen-markdown/options.schema.json +16 -0
- package/plugins/gen-markdown/plugin.go +39 -0
- package/plugins/gen-markdown/quality_test.go +176 -0
- package/plugins/gen-markdown/redis_test.go +38 -0
- package/plugins/gen-markdown/render.go +344 -0
- package/plugins/gen-markdown/render_test.go +186 -0
- package/plugins/gen-markdown/service.go +456 -0
- package/plugins/gen-markdown/source.go +177 -0
- package/plugins/gen-markdown/store.go +201 -0
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +17 -0
- package/plugins/gen-mermaid/main.go +22 -0
- package/plugins/gen-mermaid/options.schema.json +10 -0
- package/plugins/gen-mermaid/plugin.go +104 -0
- package/plugins/gen-mermaid/plugin_test.go +33 -0
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/pyplugin/catalog.py +209 -0
- package/plugins/pyplugin/celery_conf.py +436 -0
- package/plugins/pyplugin/celery_tasks.py +76 -0
- package/plugins/pyplugin/kafka.py +570 -0
- package/plugins/pyplugin/names.py +38 -0
- package/plugins/pyplugin/protocol.py +79 -0
- package/plugins/pyplugin/source.py +285 -0
- package/plugins/verify-codeowners/describe.go +19 -0
- package/plugins/verify-codeowners/describe_test.go +11 -0
- package/plugins/verify-codeowners/main.go +75 -0
- package/plugins/verify-codeowners/match.go +85 -0
- package/plugins/verify-codeowners/match_test.go +47 -0
- package/plugins/verify-codeowners/options.schema.json +15 -0
- package/plugins/verify-codeowners/owners.go +164 -0
- package/plugins/verify-codeowners/owners_test.go +225 -0
- package/plugins/verify-codeowners/parse.go +90 -0
- package/plugins/verify-codeowners/parse_test.go +62 -0
- package/plugins/verify-otel/describe.go +19 -0
- package/plugins/verify-otel/describe_test.go +11 -0
- package/plugins/verify-otel/main.go +53 -0
- package/plugins/verify-otel/match.go +336 -0
- package/plugins/verify-otel/options.schema.json +30 -0
- package/plugins/verify-otel/otlp.go +200 -0
- package/plugins/verify-otel/verify.go +734 -0
- package/plugins/verify-otel/verify_test.go +460 -0
- package/portolan.json +1073 -0
- package/public/favicon.svg +12 -0
- package/schema/portolan.schema.json +2558 -0
- package/scripts/README.md +53 -0
- package/scripts/build-report.mjs +60 -0
- package/scripts/build-report.test.mjs +44 -0
- package/scripts/builtin-plugins.mjs +49 -0
- package/scripts/catalog-sources.mjs +67 -0
- package/scripts/diff.mjs +315 -0
- package/scripts/diff.test.mjs +151 -0
- package/scripts/forge-comment.mjs +144 -0
- package/scripts/forge-comment.test.mjs +151 -0
- package/scripts/forge-release.mjs +138 -0
- package/scripts/forge-release.test.mjs +106 -0
- package/scripts/forge.mjs +61 -0
- package/scripts/gen-likec4.mjs +854 -0
- package/scripts/gen-likec4.test.mjs +258 -0
- package/scripts/gen.mjs +603 -0
- package/scripts/generated-docs.test.mjs +66 -0
- package/scripts/local-api.mjs +1192 -0
- package/scripts/local-api.test.mjs +387 -0
- package/scripts/manifest.mjs +214 -0
- package/scripts/manifest.test.mjs +148 -0
- package/scripts/output-path.mjs +79 -0
- package/scripts/output-path.test.mjs +45 -0
- package/scripts/package-smoke.mjs +37 -0
- package/scripts/plugin-host.mjs +419 -0
- package/scripts/plugin-host.test.mjs +109 -0
- package/scripts/plugin-wasm-worker.mjs +34 -0
- package/scripts/run-builtin.mjs +81 -0
- package/scripts/schema.mjs +357 -0
- package/scripts/site-docs.mjs +295 -0
- package/scripts/site-docs.test.mjs +181 -0
- package/scripts/vendor-lock.mjs +58 -0
- package/scripts/vendor-lock.test.mjs +69 -0
- package/src/README.md +38 -0
- package/src/app/App.tsx +448 -0
- package/src/app/BranchPicker.test.ts +46 -0
- package/src/app/BranchPicker.tsx +153 -0
- package/src/app/Breadcrumbs.test.ts +38 -0
- package/src/app/Breadcrumbs.tsx +223 -0
- package/src/app/BuildStamp.tsx +146 -0
- package/src/app/CommandPalette.tsx +292 -0
- package/src/app/HashScroll.tsx +45 -0
- package/src/app/Sidebar.tsx +1742 -0
- package/src/app/TopBar.tsx +393 -0
- package/src/app/density.tsx +64 -0
- package/src/app/forge-access.test.ts +27 -0
- package/src/app/forge-access.tsx +80 -0
- package/src/app/panels.tsx +141 -0
- package/src/app/pins.tsx +242 -0
- package/src/app/query-client.ts +24 -0
- package/src/app/responsive.ts +55 -0
- package/src/app/shortcuts.test.ts +39 -0
- package/src/app/shortcuts.tsx +282 -0
- package/src/app/theme.tsx +51 -0
- package/src/app/title.test.ts +14 -0
- package/src/app/title.ts +30 -0
- package/src/app/toast.tsx +59 -0
- package/src/app/tree-keys.test.ts +80 -0
- package/src/app/tree-keys.ts +87 -0
- package/src/app/ui-store.ts +52 -0
- package/src/catalog-error.test.ts +105 -0
- package/src/catalog-profile.test.ts +59 -0
- package/src/catalog-profile.ts +101 -0
- package/src/catalog-stores.test.ts +431 -0
- package/src/catalog.test.ts +1215 -0
- package/src/catalog.ts +3125 -0
- package/src/chat/Cards.tsx +408 -0
- package/src/chat/ChatPanel.tsx +79 -0
- package/src/chat/ChatSettings.tsx +219 -0
- package/src/chat/Composer.tsx +99 -0
- package/src/chat/Conversation.tsx +281 -0
- package/src/chat/Header.tsx +46 -0
- package/src/chat/Message.tsx +49 -0
- package/src/chat/Notice.tsx +195 -0
- package/src/chat/Starter.tsx +67 -0
- package/src/chat/Steps.tsx +45 -0
- package/src/chat/Waiting.tsx +87 -0
- package/src/chat/flags.test.ts +59 -0
- package/src/chat/flags.ts +155 -0
- package/src/chat/linkify.test.ts +30 -0
- package/src/chat/linkify.ts +45 -0
- package/src/chat/prefs.ts +56 -0
- package/src/chat/prompt.test.ts +43 -0
- package/src/chat/prompt.ts +157 -0
- package/src/chat/store.ts +35 -0
- package/src/chat/tools.ts +58 -0
- package/src/chat/transport.ts +70 -0
- package/src/components/AdrRow.tsx +29 -0
- package/src/components/AnchorLink.tsx +67 -0
- package/src/components/ApiReference.tsx +134 -0
- package/src/components/AsyncApiReference.tsx +120 -0
- package/src/components/CatalogStamp.tsx +155 -0
- package/src/components/ChannelRows.tsx +132 -0
- package/src/components/CommandRows.tsx +139 -0
- package/src/components/DiagramSkeleton.tsx +71 -0
- package/src/components/EditorLink.tsx +85 -0
- package/src/components/FieldTree.tsx +305 -0
- package/src/components/FlowTrigger.tsx +34 -0
- package/src/components/Ident.tsx +89 -0
- package/src/components/Integrations.tsx +243 -0
- package/src/components/LifecycleDiagram.tsx +110 -0
- package/src/components/MachineDocs.tsx +42 -0
- package/src/components/Markdown.tsx +173 -0
- package/src/components/MarkdownTable.tsx +206 -0
- package/src/components/Mermaid.tsx +211 -0
- package/src/components/MethodRows.tsx +309 -0
- package/src/components/Overlay.tsx +125 -0
- package/src/components/PageHeader.tsx +189 -0
- package/src/components/RowActions.tsx +97 -0
- package/src/components/SchemaDocument.tsx +89 -0
- package/src/components/Select.tsx +126 -0
- package/src/components/ShapeRows.tsx +118 -0
- package/src/components/SourceDoc.tsx +82 -0
- package/src/components/SourcePreview.tsx +441 -0
- package/src/components/TabRow.tsx +182 -0
- package/src/components/TechIcon.tsx +34 -0
- package/src/components/Toc.tsx +132 -0
- package/src/components/WhatLinksHere.tsx +273 -0
- package/src/components/WsdlReference.tsx +84 -0
- package/src/components/ddd-icons.tsx +107 -0
- package/src/components/kind.tsx +157 -0
- package/src/components/logo.tsx +61 -0
- package/src/components/primitives.tsx +264 -0
- package/src/data.test.ts +18 -0
- package/src/data.ts +202 -0
- package/src/enrich.test.ts +1150 -0
- package/src/enrich.ts +672 -0
- package/src/er/ErCanvas.tsx +533 -0
- package/src/er/RedisSchema.tsx +74 -0
- package/src/er/RelationCard.tsx +267 -0
- package/src/er/StoreHeader.tsx +155 -0
- package/src/er/TableNode.tsx +84 -0
- package/src/er/ViewNode.tsx +83 -0
- package/src/er/layout.test.ts +115 -0
- package/src/er/layout.ts +77 -0
- package/src/er/lineage.test.ts +140 -0
- package/src/er/lineage.ts +107 -0
- package/src/er/markers.tsx +79 -0
- package/src/er/spec.test.ts +344 -0
- package/src/er/spec.ts +426 -0
- package/src/flow/ChainList.tsx +162 -0
- package/src/flow/FlowTable.tsx +123 -0
- package/src/flow/FlowToolbar.tsx +372 -0
- package/src/flow/StepDetail.tsx +380 -0
- package/src/flow/StepRail.tsx +428 -0
- package/src/flow/answers.test.ts +50 -0
- package/src/flow/answers.ts +58 -0
- package/src/flow/chain.test.ts +383 -0
- package/src/flow/chain.ts +353 -0
- package/src/flow/chapters.test.ts +185 -0
- package/src/flow/chapters.ts +225 -0
- package/src/flow/continues.test.ts +102 -0
- package/src/flow/continues.ts +65 -0
- package/src/flow/cross-context.test.ts +97 -0
- package/src/flow/cross-context.ts +35 -0
- package/src/flow/mermaid.test.ts +22 -0
- package/src/flow/mermaid.ts +141 -0
- package/src/flow/outline.test.ts +212 -0
- package/src/flow/outline.ts +170 -0
- package/src/flow/paths.test.ts +129 -0
- package/src/flow/paths.ts +147 -0
- package/src/flow/prefs.test.ts +80 -0
- package/src/flow/prefs.ts +104 -0
- package/src/graph/DependencyGraph.tsx +283 -0
- package/src/graph/DependencyNodes.tsx +162 -0
- package/src/graph/EdgeLabel.tsx +61 -0
- package/src/graph/FocusedEventGraph.tsx +211 -0
- package/src/graph/GraphToolbar.tsx +187 -0
- package/src/graph/RoutedEdge.tsx +80 -0
- package/src/graph/dependency-layout.test.ts +360 -0
- package/src/graph/dependency-layout.ts +444 -0
- package/src/graph/elk.test.ts +70 -0
- package/src/graph/elk.ts +287 -0
- package/src/graph/nodes.tsx +97 -0
- package/src/graph/theme.ts +81 -0
- package/src/graph/useElkFlow.ts +125 -0
- package/src/index.css +1547 -0
- package/src/language/InLanguage.tsx +40 -0
- package/src/language/TermCard.tsx +132 -0
- package/src/language/cards.test.ts +114 -0
- package/src/language/cards.ts +111 -0
- package/src/lib/admonition.test.ts +49 -0
- package/src/lib/admonition.ts +48 -0
- package/src/lib/adr.test.ts +131 -0
- package/src/lib/adr.ts +68 -0
- package/src/lib/api.test.ts +122 -0
- package/src/lib/api.ts +135 -0
- package/src/lib/backlinks.test.ts +330 -0
- package/src/lib/backlinks.ts +567 -0
- package/src/lib/branch-compare.ts +16 -0
- package/src/lib/build-info.test.ts +76 -0
- package/src/lib/build-info.ts +80 -0
- package/src/lib/catalog-diff.test.ts +275 -0
- package/src/lib/catalog-diff.ts +655 -0
- package/src/lib/clipboard.ts +43 -0
- package/src/lib/comparison-memory.test.ts +40 -0
- package/src/lib/comparison-memory.ts +39 -0
- package/src/lib/context-color.ts +27 -0
- package/src/lib/context-map.test.ts +148 -0
- package/src/lib/context-map.ts +399 -0
- package/src/lib/data-model.test.ts +245 -0
- package/src/lib/data-model.ts +399 -0
- package/src/lib/data-problems.test.ts +178 -0
- package/src/lib/data-problems.ts +314 -0
- package/src/lib/derive.test.ts +251 -0
- package/src/lib/derive.ts +515 -0
- package/src/lib/editor-link.test.ts +125 -0
- package/src/lib/editor-link.ts +96 -0
- package/src/lib/editor-prefs.ts +27 -0
- package/src/lib/event-graph.test.ts +237 -0
- package/src/lib/event-graph.ts +327 -0
- package/src/lib/export-canvas.ts +37 -0
- package/src/lib/export-file.test.ts +30 -0
- package/src/lib/export-file.ts +20 -0
- package/src/lib/flow-tree.test.ts +177 -0
- package/src/lib/flow-tree.ts +160 -0
- package/src/lib/forge-refs.test.ts +50 -0
- package/src/lib/forge-refs.ts +60 -0
- package/src/lib/format.test.ts +35 -0
- package/src/lib/format.ts +64 -0
- package/src/lib/github-catalog.test.ts +301 -0
- package/src/lib/github-catalog.ts +610 -0
- package/src/lib/integrations.test.ts +119 -0
- package/src/lib/integrations.ts +133 -0
- package/src/lib/kinds.test.ts +91 -0
- package/src/lib/kinds.ts +204 -0
- package/src/lib/lifecycle.test.ts +93 -0
- package/src/lib/lifecycle.ts +165 -0
- package/src/lib/local-api.ts +194 -0
- package/src/lib/motion-features.ts +11 -0
- package/src/lib/motion.test.ts +120 -0
- package/src/lib/motion.tsx +269 -0
- package/src/lib/palette-recent.test.ts +80 -0
- package/src/lib/palette-recent.ts +90 -0
- package/src/lib/palette.test.ts +365 -0
- package/src/lib/palette.ts +593 -0
- package/src/lib/pins.test.ts +139 -0
- package/src/lib/pins.ts +130 -0
- package/src/lib/problems.test.ts +112 -0
- package/src/lib/project-preview.ts +5 -0
- package/src/lib/proto-problems.test.ts +121 -0
- package/src/lib/proto-problems.ts +65 -0
- package/src/lib/queries.test.ts +113 -0
- package/src/lib/queries.ts +131 -0
- package/src/lib/registry.test.ts +219 -0
- package/src/lib/registry.ts +208 -0
- package/src/lib/scenarios.ts +547 -0
- package/src/lib/setup-info.test.ts +199 -0
- package/src/lib/setup-info.ts +415 -0
- package/src/lib/shape.test.ts +289 -0
- package/src/lib/shape.ts +454 -0
- package/src/lib/sidebar-prefs.test.ts +32 -0
- package/src/lib/sidebar-prefs.ts +75 -0
- package/src/lib/source-code.test.ts +159 -0
- package/src/lib/source-code.ts +286 -0
- package/src/lib/source-doc.test.ts +131 -0
- package/src/lib/source-doc.ts +103 -0
- package/src/lib/source-highlight.test.ts +29 -0
- package/src/lib/source-highlight.ts +24 -0
- package/src/lib/source-link.test.ts +153 -0
- package/src/lib/source-link.ts +201 -0
- package/src/lib/spec-files.test.ts +24 -0
- package/src/lib/spec-files.ts +23 -0
- package/src/lib/table-directive.test.ts +139 -0
- package/src/lib/table-directive.ts +106 -0
- package/src/lib/tech.test.ts +120 -0
- package/src/lib/tech.ts +421 -0
- package/src/lib/terms.test.ts +67 -0
- package/src/lib/terms.ts +179 -0
- package/src/lib/wire-problems.test.ts +263 -0
- package/src/lib/wire-problems.ts +281 -0
- package/src/likec4/C4View.tsx +55 -0
- package/src/likec4/CanvasBridge.tsx +187 -0
- package/src/likec4/FlowView.tsx +235 -0
- package/src/likec4/InteractiveView.tsx +144 -0
- package/src/likec4/canvas-viewport.test.ts +149 -0
- package/src/likec4/canvas-viewport.ts +135 -0
- package/src/likec4/flow-edges.test.ts +51 -0
- package/src/likec4/flow-edges.ts +89 -0
- package/src/likec4/frame-theme.test.ts +76 -0
- package/src/likec4/frame-theme.ts +101 -0
- package/src/likec4/highlight-css.test.ts +49 -0
- package/src/likec4/highlight-css.ts +52 -0
- package/src/likec4/ids.test.ts +151 -0
- package/src/likec4/ids.ts +115 -0
- package/src/likec4/levels.tsx +70 -0
- package/src/likec4/mapping.test.ts +66 -0
- package/src/likec4/mapping.ts +54 -0
- package/src/likec4/reserved.json +141 -0
- package/src/likec4/view-index.ts +78 -0
- package/src/main.tsx +13 -0
- package/src/map/ContextMapGraph.tsx +274 -0
- package/src/map/ContextNode.tsx +70 -0
- package/src/map/FloatingEdge.tsx +86 -0
- package/src/merge.test.ts +1038 -0
- package/src/merge.ts +1004 -0
- package/src/pages/AdrDetail.tsx +242 -0
- package/src/pages/AdrIndex.tsx +114 -0
- package/src/pages/AggregatePage.tsx +727 -0
- package/src/pages/BlockPage.tsx +478 -0
- package/src/pages/CatalogFailure.tsx +111 -0
- package/src/pages/Changes.tsx +313 -0
- package/src/pages/ContextMap.tsx +534 -0
- package/src/pages/ContextPage.tsx +346 -0
- package/src/pages/EnumPage.tsx +282 -0
- package/src/pages/EventPage.tsx +686 -0
- package/src/pages/ExternalPage.tsx +204 -0
- package/src/pages/FlowDetail.tsx +663 -0
- package/src/pages/FlowIndex.tsx +303 -0
- package/src/pages/GraphPage.tsx +217 -0
- package/src/pages/Language.tsx +257 -0
- package/src/pages/ModulePage.tsx +366 -0
- package/src/pages/NotFound.tsx +54 -0
- package/src/pages/Overview.tsx +344 -0
- package/src/pages/Problems.tsx +360 -0
- package/src/pages/RegistryIndex.tsx +118 -0
- package/src/pages/ServicePage.tsx +746 -0
- package/src/pages/Settings.tsx +975 -0
- package/src/pages/StorePage.tsx +114 -0
- package/src/peek/PeekCard.test.ts +35 -0
- package/src/peek/PeekCard.tsx +118 -0
- package/src/peek/PeekLayer.tsx +147 -0
- package/src/peek/model.test.ts +156 -0
- package/src/peek/model.ts +253 -0
- package/src/routes.test.ts +222 -0
- package/src/routes.ts +449 -0
- package/src/selection/DetailPanel.tsx +1407 -0
- package/src/selection/hash.test.ts +73 -0
- package/src/selection/hash.ts +58 -0
- package/src/selection/model.test.ts +199 -0
- package/src/selection/model.ts +473 -0
- package/src/selection/pages.test.ts +115 -0
- package/src/selection/pages.ts +268 -0
- package/src/selection/store.test.ts +62 -0
- package/src/selection/store.ts +46 -0
- package/src/selection/sync.tsx +99 -0
- package/src/table/ColumnsMenu.tsx +72 -0
- package/src/table/DataTable.tsx +780 -0
- package/src/table/Facets.tsx +54 -0
- package/src/table/GroupMenu.tsx +69 -0
- package/src/table/Toolbar.tsx +190 -0
- package/src/table/cells.tsx +117 -0
- package/src/table/compare.test.ts +117 -0
- package/src/table/compare.ts +196 -0
- package/src/table/export.test.ts +88 -0
- package/src/table/export.ts +66 -0
- package/src/table/facet-groups.test.ts +74 -0
- package/src/table/facet-groups.ts +56 -0
- package/src/table/grouping.test.ts +84 -0
- package/src/table/grouping.ts +82 -0
- package/src/table/infer.test.ts +57 -0
- package/src/table/infer.ts +50 -0
- package/src/table/persist.test.ts +76 -0
- package/src/table/persist.ts +112 -0
- package/src/table/sort-url.test.ts +75 -0
- package/src/table/sort-url.ts +52 -0
- package/src/table/types.ts +80 -0
- package/src/table/useTableState.ts +220 -0
- package/src/test-catalog.ts +7 -0
- package/src/testing/estate/data__catalog.json +2847 -0
- package/src/testing/estate/data__flows.json +1724 -0
- package/src/testing/estate/examples__auth__portolan__api.json +212 -0
- package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
- package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
- package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
- package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
- package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
- package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
- package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
- package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
- package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
- package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
- package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
- package/src/testing/estate.ts +49 -0
- package/src/trail/Trail.tsx +81 -0
- package/src/trail/model.test.ts +157 -0
- package/src/trail/model.ts +189 -0
- package/src/trail/record.tsx +32 -0
- package/src/trail/store.test.ts +66 -0
- package/src/trail/store.ts +102 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +171 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"""Where a task goes: the app's configuration, read as syntax.
|
|
2
|
+
|
|
3
|
+
A Django project configures Celery in one of two places, and usually both: the
|
|
4
|
+
app module, `app = Celery("billing")` followed by `app.conf.update(...)` or
|
|
5
|
+
`app.conf.task_routes = ...`, and the settings module the app is told to read
|
|
6
|
+
with `config_from_object("django.conf:settings", namespace="CELERY")`, where
|
|
7
|
+
every key is spelled in upper case under that prefix. Both are read here, the
|
|
8
|
+
settings first and the app's own assignments over them, which is the order
|
|
9
|
+
Celery applies them in.
|
|
10
|
+
|
|
11
|
+
Three keys decide a queue: `task_routes`, `task_default_queue` and
|
|
12
|
+
`broker_url`. A fourth, `beat_schedule`, says what the clock sets off - and
|
|
13
|
+
so does `app.add_periodic_task(...)`, wherever it is called. A value that is
|
|
14
|
+
not a literal is read as far as it goes - `os.environ.get("X", default)` is
|
|
15
|
+
its default - and past that it is unknown, which is said rather than guessed.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import ast
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from fnmatch import fnmatchcase
|
|
23
|
+
from typing import Dict, List, Optional, Tuple
|
|
24
|
+
|
|
25
|
+
from source import Module, Project, assigned, const_str, dotted, keyword, keyword_str
|
|
26
|
+
|
|
27
|
+
# Celery's own default queue, used when nothing in the tree says otherwise.
|
|
28
|
+
DEFAULT_QUEUE = "celery"
|
|
29
|
+
|
|
30
|
+
# The pre-4.0 spellings, still accepted by Celery and still found in settings.
|
|
31
|
+
LEGACY = {
|
|
32
|
+
"CELERY_ROUTES": "task_routes",
|
|
33
|
+
"CELERY_DEFAULT_QUEUE": "task_default_queue",
|
|
34
|
+
"BROKER_URL": "broker_url",
|
|
35
|
+
"CELERYBEAT_SCHEDULE": "beat_schedule",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
KNOWN = {"task_routes", "task_default_queue", "broker_url", "beat_schedule"}
|
|
39
|
+
|
|
40
|
+
# The application that keeps the schedule in the database instead of the tree.
|
|
41
|
+
BEAT_IN_DATABASE = "django_celery_beat"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class App:
|
|
46
|
+
"""A `Celery(...)` instance: the module it lives in and the name it is
|
|
47
|
+
bound to there, which is what `@app.task` is resolved against."""
|
|
48
|
+
|
|
49
|
+
module: Module
|
|
50
|
+
name: str
|
|
51
|
+
node: ast.Call
|
|
52
|
+
namespace: str = ""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Schedule:
|
|
57
|
+
"""One thing the clock sets off: a `beat_schedule` entry, or one call of
|
|
58
|
+
`add_periodic_task`. The task is named on the wire (`task`) or as the
|
|
59
|
+
signature spells it (`ref`, resolved by whoever holds the task index)."""
|
|
60
|
+
|
|
61
|
+
name: str # the entry's key, or the `name=` the call gave
|
|
62
|
+
when: str # "cron 0 9 * * *", "every 6h" - or the expression as written, when opaque
|
|
63
|
+
module: Module # where it is declared
|
|
64
|
+
node: ast.AST
|
|
65
|
+
task: str = "" # the wire name, when the entry says one
|
|
66
|
+
ref: str = "" # the task as `add_periodic_task` names it, when it is a signature
|
|
67
|
+
queue: str = "" # `options={"queue": ...}` or a `queue=` on the call
|
|
68
|
+
opaque: bool = False # the schedule was not readable as syntax
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def where(self) -> str:
|
|
72
|
+
return self.module.where(self.node)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class Config:
|
|
77
|
+
routes: List[Tuple[str, str]] = field(default_factory=list) # pattern -> queue, in declaration order
|
|
78
|
+
default_queue: str = ""
|
|
79
|
+
broker: str = ""
|
|
80
|
+
settings: str = "" # the settings module, as looked for
|
|
81
|
+
settings_found: bool = False
|
|
82
|
+
opaque: List[str] = field(default_factory=list) # where a value could not be read
|
|
83
|
+
apps: List[App] = field(default_factory=list)
|
|
84
|
+
schedules: List[Schedule] = field(default_factory=list) # in declaration order
|
|
85
|
+
opaque_schedules: List[str] = field(default_factory=list) # where a beat_schedule was not a mapping
|
|
86
|
+
beat_in_database: bool = False # django_celery_beat is installed
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def broker_scheme(self) -> str:
|
|
90
|
+
return self.broker.split("://", 1)[0] if "://" in self.broker else ""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def find_apps(project: Project) -> List[App]:
|
|
94
|
+
"""Every module-level `X = Celery(...)`, in path order."""
|
|
95
|
+
out = []
|
|
96
|
+
for module in sorted(project.modules.values(), key=lambda m: m.rel):
|
|
97
|
+
for name, value, _ in assigned(module.tree):
|
|
98
|
+
if isinstance(value, ast.Call) and dotted(value.func).split(".")[-1] == "Celery":
|
|
99
|
+
out.append(App(module, name, value))
|
|
100
|
+
return out
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def settings_module_name(project: Project) -> str:
|
|
104
|
+
"""What `manage.py` sets `DJANGO_SETTINGS_MODULE` to, or the app module
|
|
105
|
+
does; the settings module is otherwise nowhere written down."""
|
|
106
|
+
for module in sorted(project.modules.values(), key=lambda m: m.rel):
|
|
107
|
+
if module.dotted.split(".")[-1] not in ("manage", "celery", "wsgi", "asgi"):
|
|
108
|
+
continue
|
|
109
|
+
for node in ast.walk(module.tree):
|
|
110
|
+
if isinstance(node, ast.Call) and dotted(node.func).split(".")[-1] == "setdefault":
|
|
111
|
+
if len(node.args) == 2 and const_str(node.args[0]) == "DJANGO_SETTINGS_MODULE":
|
|
112
|
+
return const_str(node.args[1])
|
|
113
|
+
if isinstance(node, ast.Assign) and len(node.targets) == 1:
|
|
114
|
+
target = node.targets[0]
|
|
115
|
+
if isinstance(target, ast.Subscript) and dotted(target.value) == "os.environ" and const_str(target.slice) == "DJANGO_SETTINGS_MODULE":
|
|
116
|
+
return const_str(node.value)
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def read_config(project: Project, settings: str) -> Config:
|
|
121
|
+
cfg = Config(apps=find_apps(project))
|
|
122
|
+
|
|
123
|
+
# The app first, for the namespace the settings are read under.
|
|
124
|
+
for app in cfg.apps:
|
|
125
|
+
for node in ast.walk(app.module.tree):
|
|
126
|
+
if isinstance(node, ast.Call) and dotted(node.func) == app.name + ".config_from_object":
|
|
127
|
+
app.namespace = const_str(keyword(node, "namespace"))
|
|
128
|
+
broker = keyword(app.node, "broker")
|
|
129
|
+
if broker is not None:
|
|
130
|
+
cfg.broker = str_value(broker, app.module) or cfg.broker
|
|
131
|
+
|
|
132
|
+
cfg.settings = settings or settings_module_name(project)
|
|
133
|
+
module = project.module(cfg.settings) if cfg.settings else None
|
|
134
|
+
if module is not None:
|
|
135
|
+
cfg.settings_found = True
|
|
136
|
+
namespaces = [app.namespace for app in cfg.apps if app.namespace]
|
|
137
|
+
for name, value, _ in assigned(module.tree):
|
|
138
|
+
key = settings_key(name, namespaces)
|
|
139
|
+
if key:
|
|
140
|
+
apply(cfg, key, value, module)
|
|
141
|
+
if name == "INSTALLED_APPS":
|
|
142
|
+
installed = follow(value, module)
|
|
143
|
+
if isinstance(installed, (ast.List, ast.Tuple)) and any(const_str(e) == BEAT_IN_DATABASE for e in installed.elts):
|
|
144
|
+
cfg.beat_in_database = True
|
|
145
|
+
|
|
146
|
+
# Then what the app module assigns itself, over the settings.
|
|
147
|
+
for app in cfg.apps:
|
|
148
|
+
for node in app.module.tree.body:
|
|
149
|
+
if isinstance(node, ast.Assign) and len(node.targets) == 1:
|
|
150
|
+
target = dotted(node.targets[0])
|
|
151
|
+
prefix = app.name + ".conf."
|
|
152
|
+
if target.startswith(prefix):
|
|
153
|
+
apply(cfg, target[len(prefix):].lower(), node.value, app.module)
|
|
154
|
+
call = node.value if isinstance(node, ast.Expr) else None
|
|
155
|
+
if isinstance(call, ast.Call) and dotted(call.func) == app.name + ".conf.update":
|
|
156
|
+
for kw in call.keywords:
|
|
157
|
+
if kw.arg:
|
|
158
|
+
apply(cfg, kw.arg.lower(), kw.value, app.module)
|
|
159
|
+
|
|
160
|
+
# `add_periodic_task` is the other way to say what the clock sets off,
|
|
161
|
+
# usually under `@app.on_after_configure.connect`, where the app arrives
|
|
162
|
+
# as `sender`; so it is read by the method's name, wherever it is called.
|
|
163
|
+
for module in sorted(project.modules.values(), key=lambda m: m.rel):
|
|
164
|
+
for node in ast.walk(module.tree):
|
|
165
|
+
if isinstance(node, ast.Call) and dotted(node.func).split(".")[-1] == "add_periodic_task" and len(node.args) >= 2:
|
|
166
|
+
cfg.schedules.append(periodic_task(node, module))
|
|
167
|
+
return cfg
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def periodic_task(call: ast.Call, module: Module) -> Schedule:
|
|
171
|
+
"""`sender.add_periodic_task(crontab(hour=3), close_stale_drafts.s(), name="...")`."""
|
|
172
|
+
when, readable = schedule_text(call.args[0], module)
|
|
173
|
+
signature = call.args[1]
|
|
174
|
+
ref = ""
|
|
175
|
+
if isinstance(signature, ast.Call) and isinstance(signature.func, ast.Attribute) and signature.func.attr in ("s", "si", "signature", "subtask"):
|
|
176
|
+
ref = dotted(signature.func.value)
|
|
177
|
+
else:
|
|
178
|
+
ref = dotted(signature)
|
|
179
|
+
name = keyword_str(call, "name") or ref.split(".")[-1]
|
|
180
|
+
return Schedule(name, when, module, call, ref=ref, queue=keyword_str(call, "queue"), opaque=not readable)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def settings_key(name: str, namespaces: List[str]) -> str:
|
|
184
|
+
"""`CELERY_TASK_ROUTES` under the `CELERY` namespace is `task_routes`;
|
|
185
|
+
without a namespace the new-style lower-case keys and the legacy upper-case
|
|
186
|
+
ones are what Celery itself accepts."""
|
|
187
|
+
for namespace in namespaces:
|
|
188
|
+
prefix = namespace + "_"
|
|
189
|
+
if name.startswith(prefix):
|
|
190
|
+
key = name[len(prefix):].lower()
|
|
191
|
+
return key if key in KNOWN else ""
|
|
192
|
+
if namespaces:
|
|
193
|
+
return ""
|
|
194
|
+
if name in LEGACY:
|
|
195
|
+
return LEGACY[name]
|
|
196
|
+
return name if name in KNOWN else ""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def apply(cfg: Config, key: str, value: ast.AST, module: Module) -> None:
|
|
200
|
+
if key == "task_routes":
|
|
201
|
+
routes = read_routes(value, module)
|
|
202
|
+
if routes is None:
|
|
203
|
+
cfg.opaque.append(module.where(value))
|
|
204
|
+
else:
|
|
205
|
+
cfg.routes = routes
|
|
206
|
+
elif key == "task_default_queue":
|
|
207
|
+
cfg.default_queue = str_value(value, module) or cfg.default_queue
|
|
208
|
+
elif key == "broker_url":
|
|
209
|
+
cfg.broker = str_value(value, module) or cfg.broker
|
|
210
|
+
elif key == "beat_schedule":
|
|
211
|
+
read_beat(cfg, value, module)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def read_beat(cfg: Config, node: ast.AST, module: Module) -> None:
|
|
215
|
+
"""`{"nightly": {"task": "pkg.tasks.nightly", "schedule": crontab(hour=2),
|
|
216
|
+
"options": {"queue": "slow"}}}`, entry by entry, in the order written."""
|
|
217
|
+
node = follow(node, module)
|
|
218
|
+
if not isinstance(node, ast.Dict):
|
|
219
|
+
cfg.opaque_schedules.append(module.where(node) if node is not None else module.rel)
|
|
220
|
+
return
|
|
221
|
+
for key, spec in zip(node.keys, node.values):
|
|
222
|
+
name = const_str(key) if key is not None else ""
|
|
223
|
+
spec = follow(spec, module)
|
|
224
|
+
if not name or not isinstance(spec, ast.Dict):
|
|
225
|
+
cfg.opaque_schedules.append(module.where(spec if spec is not None else node))
|
|
226
|
+
continue
|
|
227
|
+
fields = {const_str(k): v for k, v in zip(spec.keys, spec.values) if k is not None and const_str(k)}
|
|
228
|
+
when, readable = schedule_text(fields.get("schedule"), module)
|
|
229
|
+
cfg.schedules.append(
|
|
230
|
+
Schedule(
|
|
231
|
+
name,
|
|
232
|
+
when,
|
|
233
|
+
module,
|
|
234
|
+
spec,
|
|
235
|
+
task=str_value(fields.get("task"), module),
|
|
236
|
+
queue=route_queue(fields["options"], module) if "options" in fields else "",
|
|
237
|
+
opaque=not readable,
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# --- a schedule, as text ----------------------------------------------------
|
|
243
|
+
#
|
|
244
|
+
# Celery's schedule is an object - a crontab, a timedelta, a number of seconds
|
|
245
|
+
# - and the page wants a sentence. The forms the documentation gives are read
|
|
246
|
+
# into the spellings a reader already knows: `cron 0 9 * * *` in crontab's own
|
|
247
|
+
# field order, `every 6h` for an interval. Anything else is kept as written and
|
|
248
|
+
# marked opaque, which the caller says out loud.
|
|
249
|
+
|
|
250
|
+
CRON_FIELDS = ("minute", "hour", "day_of_month", "month_of_year", "day_of_week")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def schedule_text(node: Optional[ast.AST], module: Module) -> Tuple[str, bool]:
|
|
254
|
+
"""(text, readable): the schedule as a sentence, and whether syntax
|
|
255
|
+
actually said so."""
|
|
256
|
+
node = follow(node, module)
|
|
257
|
+
if node is None:
|
|
258
|
+
return "", False
|
|
259
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)) and not isinstance(node.value, bool):
|
|
260
|
+
return "every " + interval(float(node.value)), True
|
|
261
|
+
if isinstance(node, ast.Call):
|
|
262
|
+
last = dotted(node.func).split(".")[-1]
|
|
263
|
+
if last == "crontab":
|
|
264
|
+
given = {kw.arg: cron_field(kw.value) for kw in node.keywords if kw.arg in CRON_FIELDS}
|
|
265
|
+
for position, arg in enumerate(node.args[: len(CRON_FIELDS)]):
|
|
266
|
+
given.setdefault(CRON_FIELDS[position], cron_field(arg))
|
|
267
|
+
if any(value is None for value in given.values()):
|
|
268
|
+
return unparse(node), False
|
|
269
|
+
return "cron " + " ".join(given.get(field_, "*") or "*" for field_ in CRON_FIELDS), True
|
|
270
|
+
if last == "timedelta":
|
|
271
|
+
seconds = timedelta_seconds(node)
|
|
272
|
+
if seconds is None:
|
|
273
|
+
return unparse(node), False
|
|
274
|
+
return "every " + interval(seconds), True
|
|
275
|
+
if last == "schedule":
|
|
276
|
+
inner = keyword(node, "run_every") if keyword(node, "run_every") is not None else (node.args[0] if node.args else None)
|
|
277
|
+
return schedule_text(inner, module)
|
|
278
|
+
if last == "solar" and node.args and const_str(node.args[0]):
|
|
279
|
+
return "solar " + const_str(node.args[0]), True
|
|
280
|
+
return unparse(node), False
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def cron_field(node: ast.AST) -> Optional[str]:
|
|
284
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, (str, int)) and not isinstance(node.value, bool):
|
|
285
|
+
return str(node.value)
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
TIMEDELTA_UNITS = {"weeks": 604800.0, "days": 86400.0, "hours": 3600.0, "minutes": 60.0, "seconds": 1.0, "milliseconds": 0.001}
|
|
290
|
+
TIMEDELTA_POSITIONS = ("days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def timedelta_seconds(call: ast.Call) -> Optional[float]:
|
|
294
|
+
total = 0.0
|
|
295
|
+
parts = [(TIMEDELTA_POSITIONS[i], arg) for i, arg in enumerate(call.args[: len(TIMEDELTA_POSITIONS)])]
|
|
296
|
+
parts += [(kw.arg or "", kw.value) for kw in call.keywords]
|
|
297
|
+
for unit, value in parts:
|
|
298
|
+
if unit not in TIMEDELTA_UNITS or not (isinstance(value, ast.Constant) and isinstance(value.value, (int, float)) and not isinstance(value.value, bool)):
|
|
299
|
+
return None
|
|
300
|
+
total += float(value.value) * TIMEDELTA_UNITS[unit]
|
|
301
|
+
return total
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def interval(seconds: float) -> str:
|
|
305
|
+
"""90 -> "1m30s", 21600 -> "6h", 0.5 -> "0.5s"."""
|
|
306
|
+
if seconds != int(seconds):
|
|
307
|
+
return "%gs" % seconds
|
|
308
|
+
whole = int(seconds)
|
|
309
|
+
out = ""
|
|
310
|
+
for unit, size in (("d", 86400), ("h", 3600), ("m", 60)):
|
|
311
|
+
if whole >= size:
|
|
312
|
+
out += "%d%s" % (whole // size, unit)
|
|
313
|
+
whole %= size
|
|
314
|
+
if whole or not out:
|
|
315
|
+
out += "%ds" % whole
|
|
316
|
+
return out
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def unparse(node: ast.AST) -> str:
|
|
320
|
+
try:
|
|
321
|
+
return ast.unparse(node)
|
|
322
|
+
except Exception: # a node ast.unparse does not know
|
|
323
|
+
return type(node).__name__
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def read_routes(node: ast.AST, module: Module) -> Optional[List[Tuple[str, str]]]:
|
|
327
|
+
"""`{"pkg.tasks.send": {"queue": "mail"}}`, or the same as a list of pairs.
|
|
328
|
+
A router function, or anything else that is not a literal, is None: the
|
|
329
|
+
routes are decided at run time and this reader does not run anything."""
|
|
330
|
+
node = follow(node, module)
|
|
331
|
+
pairs: List[Tuple[ast.AST, ast.AST]] = []
|
|
332
|
+
if isinstance(node, ast.Dict):
|
|
333
|
+
pairs = [(k, v) for k, v in zip(node.keys, node.values) if k is not None]
|
|
334
|
+
elif isinstance(node, (ast.List, ast.Tuple)):
|
|
335
|
+
for element in node.elts:
|
|
336
|
+
if isinstance(element, ast.Tuple) and len(element.elts) == 2:
|
|
337
|
+
pairs.append((element.elts[0], element.elts[1]))
|
|
338
|
+
elif isinstance(element, ast.Dict):
|
|
339
|
+
pairs += [(k, v) for k, v in zip(element.keys, element.values) if k is not None]
|
|
340
|
+
else:
|
|
341
|
+
return None
|
|
342
|
+
else:
|
|
343
|
+
return None
|
|
344
|
+
out = []
|
|
345
|
+
for key, spec in pairs:
|
|
346
|
+
pattern = const_str(key)
|
|
347
|
+
queue = route_queue(spec, module)
|
|
348
|
+
if pattern and queue:
|
|
349
|
+
out.append((pattern, queue))
|
|
350
|
+
return out
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def route_queue(spec: ast.AST, module: Module) -> str:
|
|
354
|
+
spec = follow(spec, module)
|
|
355
|
+
if isinstance(spec, ast.Dict):
|
|
356
|
+
for key, value in zip(spec.keys, spec.values):
|
|
357
|
+
if key is not None and const_str(key) == "queue":
|
|
358
|
+
return str_value(value, module)
|
|
359
|
+
return ""
|
|
360
|
+
return str_value(spec, module)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def str_value(node: Optional[ast.AST], module: Module) -> str:
|
|
364
|
+
"""A string as far as syntax carries it: the literal, the default of an
|
|
365
|
+
environment lookup, or a module-level constant, followed once."""
|
|
366
|
+
node = follow(node, module)
|
|
367
|
+
if node is None:
|
|
368
|
+
return ""
|
|
369
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, str):
|
|
370
|
+
return node.value
|
|
371
|
+
if isinstance(node, ast.Call):
|
|
372
|
+
name = dotted(node.func)
|
|
373
|
+
last = name.split(".")[-1]
|
|
374
|
+
if last in ("get", "getenv") and (name.startswith("os.environ") or name == "os.getenv" or name == "getenv"):
|
|
375
|
+
default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
|
|
376
|
+
return str_value(default, module)
|
|
377
|
+
if last in ("env", "str") and name.split(".")[0] in ("env", "environ", "config"):
|
|
378
|
+
default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
|
|
379
|
+
return str_value(default, module)
|
|
380
|
+
return ""
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def follow(node: Optional[ast.AST], module: Module) -> Optional[ast.AST]:
|
|
384
|
+
"""A bare name is the module-level assignment it refers to, once."""
|
|
385
|
+
if isinstance(node, ast.Name):
|
|
386
|
+
for name, value, _ in assigned(module.tree):
|
|
387
|
+
if name == node.id:
|
|
388
|
+
return value
|
|
389
|
+
return node
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def apps_by_module(cfg: Config) -> Dict[Tuple[str, str], App]:
|
|
393
|
+
return {(app.module.dotted, app.name): app for app in cfg.apps}
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
# --- which queue a task lands on, decided the way Celery decides it ---------
|
|
397
|
+
#
|
|
398
|
+
# The call comes first, `apply_async(queue=...)`; then the decorator's `queue=`;
|
|
399
|
+
# then `task_routes`, an exact name before a glob, in the order they are
|
|
400
|
+
# written; then `task_default_queue`; and past all of those Celery's own
|
|
401
|
+
# `celery`. Every answer says which rule gave it, so a page can say "routed by
|
|
402
|
+
# task_routes" rather than leave a reader to work it out.
|
|
403
|
+
|
|
404
|
+
from typing import List, Tuple
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
AT_CALL = "named at the call"
|
|
408
|
+
ON_TASK = "named on the task"
|
|
409
|
+
BY_ROUTES = "routed by task_routes"
|
|
410
|
+
BY_DEFAULT = "the default queue"
|
|
411
|
+
BY_CELERY = "Celery's own default"
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def queue_for(wire: str, at_call: str, on_task: str, cfg: Config) -> Tuple[str, str]:
|
|
415
|
+
if at_call:
|
|
416
|
+
return at_call, AT_CALL
|
|
417
|
+
if on_task:
|
|
418
|
+
return on_task, ON_TASK
|
|
419
|
+
for pattern, queue in cfg.routes:
|
|
420
|
+
if matches(pattern, wire):
|
|
421
|
+
return queue, BY_ROUTES
|
|
422
|
+
if cfg.default_queue:
|
|
423
|
+
return cfg.default_queue, BY_DEFAULT
|
|
424
|
+
return DEFAULT_QUEUE, BY_CELERY
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def matches(pattern: str, wire: str) -> bool:
|
|
428
|
+
"""Celery takes a glob - `invoices.tasks.*` - and a compiled regex; only
|
|
429
|
+
the glob is syntax, so only the glob is matched here."""
|
|
430
|
+
return pattern == wire or fnmatchcase(wire, pattern)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def unmatched(cfg: Config, wires: List[str]) -> List[str]:
|
|
434
|
+
"""Route patterns that name no task in the tree: a route to nowhere is
|
|
435
|
+
stale, or the task moved."""
|
|
436
|
+
return [pattern for pattern, _ in cfg.routes if not any(matches(pattern, wire) for wire in wires)]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""The tasks: every function the tree hands to Celery.
|
|
2
|
+
|
|
3
|
+
A task is a module-level function decorated `@shared_task`, `@app.task` or
|
|
4
|
+
`@<anything>.task`, with or without arguments. Its wire name is the `name=` it
|
|
5
|
+
was given, or what Celery composes when it was not: the module's dotted path
|
|
6
|
+
and the function's name, `invoices.tasks.send_invoice_email`. A `queue=` on
|
|
7
|
+
the decorator is where it goes unless a call says otherwise; the docstring is
|
|
8
|
+
what the page says about it. A class-based task is not read.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import ast
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import Dict, List, Tuple
|
|
16
|
+
|
|
17
|
+
from source import Module, Project, decorator_named, doc, keyword_str
|
|
18
|
+
|
|
19
|
+
DECORATORS = ("task", "shared_task")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def package_of(module: Module) -> str:
|
|
23
|
+
"""The module as an import spells it: a package's `__init__` is the package."""
|
|
24
|
+
return module.dotted[: -len(".__init__")] if module.dotted.endswith(".__init__") else module.dotted
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Task:
|
|
29
|
+
module: Module
|
|
30
|
+
node: ast.AST
|
|
31
|
+
name: str # the wire name
|
|
32
|
+
queue: str # the decorator's, "" when it has none
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def short(self) -> str:
|
|
36
|
+
return self.node.name # type: ignore[attr-defined]
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def key(self) -> Tuple[str, str]:
|
|
40
|
+
return package_of(self.module), self.short
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def line(self) -> str:
|
|
44
|
+
return self.module.where(self.node)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def doc(self) -> str:
|
|
48
|
+
return doc(self.node)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def entrypoint(self) -> str:
|
|
52
|
+
"""Stable source seam shared by the task transport and body readers."""
|
|
53
|
+
return "python:%s:%s" % (package_of(self.module), self.short)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_tasks(project: Project) -> List[Task]:
|
|
57
|
+
out = []
|
|
58
|
+
for module in sorted(project.modules.values(), key=lambda m: m.rel):
|
|
59
|
+
for fn in module.functions():
|
|
60
|
+
dec = decorator_named(fn, *DECORATORS)
|
|
61
|
+
if dec is None:
|
|
62
|
+
continue
|
|
63
|
+
name = queue = ""
|
|
64
|
+
if isinstance(dec, ast.Call):
|
|
65
|
+
name = keyword_str(dec, "name")
|
|
66
|
+
queue = keyword_str(dec, "queue")
|
|
67
|
+
out.append(Task(module, fn, name or package_of(module) + "." + fn.name, queue))
|
|
68
|
+
return out
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def index(tasks: List[Task]) -> Tuple[Dict[Tuple[str, str], Task], Dict[str, Task]]:
|
|
72
|
+
"""By where the function is, for a call on it; by wire name, for
|
|
73
|
+
`send_task`."""
|
|
74
|
+
by_key = {task.key: task for task in tasks}
|
|
75
|
+
by_name = {task.name: task for task in tasks}
|
|
76
|
+
return by_key, by_name
|