feat: add direnv/devbox configurations and fix tests

This commit is contained in:
BJ Dierkes 2025-06-10 01:44:15 -05:00
parent 23b9b95d93
commit 8b038170d8
10 changed files with 1916 additions and 112 deletions

14
.envrc Normal file
View File

@ -0,0 +1,14 @@
# Automatically sets up your devbox environment whenever you cd into this
# directory via our direnv integration:
eval "$(devbox generate direnv --print-envrc)"
# check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/
# for more details
source_env_if_exists .envrc.local
export SMTP_HOST=localhost
export SMTP_PORT=1025
export MEMCACHED_HOST=localhost
export REDIS_HOST=localhost

2
.gitignore vendored
View File

@ -52,7 +52,7 @@ pip-log.txt
# Documentation
doc/build
# Unit test / coverage reports
.coverage
.coverage*
htmlcov
coverage-report
.tox

View File

@ -15,7 +15,6 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `pdm run pytest --cov=cement.core tests/core` - Test only core components
**Development Environment:**
- `make dev` - Start Docker development environment (recommended)
- `pdm venv create && pdm install` - Set up local development environment
- `pdm run cement --help` - Run the cement CLI
@ -61,7 +60,6 @@ Cement is a CLI application framework built around a handler/interface pattern w
- 100% test coverage required (pytest with coverage reporting)
- 100% PEP8 compliance enforced via ruff
- Type annotation compliance via mypy
- Docker-first development approach
- PDM for dependency management
- Zero external dependencies for core framework (optional for extensions)
@ -70,7 +68,6 @@ Cement is a CLI application framework built around a handler/interface pattern w
- Tests located in `tests/` directory mirroring source structure
- Core tests can run independently via `make test-core`
- Coverage reports generated in `coverage-report/` directory
- Tests must pass on Python 3.8+ (see docker containers for multiple versions)
## Extension Development
@ -78,4 +75,4 @@ When working with extensions:
- Check `cement/ext/` for existing extension patterns
- Optional dependencies declared in pyproject.toml under `[project.optional-dependencies]`
- Extensions follow naming pattern `ext_<name>.py`
- Must implement proper interface contracts
- Must implement proper interface contracts

1316
devbox.d/redis/redis.conf Normal file

File diff suppressed because it is too large Load Diff

24
devbox.json Normal file
View File

@ -0,0 +1,24 @@
{
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.0/.schema/devbox.schema.json",
"packages": {
"python": "3.13",
"pdm": "latest",
"libmemcached": "latest",
"zlib": {
"version": "latest",
"outputs": ["out", "dev"]
},
"redis": "latest",
"memcached": {
"version": "latest",
"outputs": ["out"]
},
"mailpit": "latest"
},
"shell": {
"init_hook": [],
"scripts": {
"test": ["echo \"Error: no test specified\" && exit 1"]
}
}
}

402
devbox.lock Normal file
View File

@ -0,0 +1,402 @@
{
"lockfile_version": "1",
"packages": {
"github:NixOS/nixpkgs/nixpkgs-unstable": {
"resolved": "github:NixOS/nixpkgs/08fcb0dcb59df0344652b38ea6326a2d8271baff?lastModified=1749401433&narHash=sha256-HXIQzULIG%2FMEUW2Q%2FSs47oE3QrjxvpUX7gUl4Xp6lnc%3D"
},
"libmemcached@latest": {
"last_modified": "2025-05-16T20:19:48Z",
"resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#libmemcached",
"source": "devbox-search",
"version": "1.0.18",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/g7mqsn59xbxcx15ry9d317qs742rqyc3-libmemcached-1.0.18",
"default": true
}
],
"store_path": "/nix/store/g7mqsn59xbxcx15ry9d317qs742rqyc3-libmemcached-1.0.18"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/3d2409gy76s14mrpp1sc3sia5h180yaq-libmemcached-1.0.18",
"default": true
}
],
"store_path": "/nix/store/3d2409gy76s14mrpp1sc3sia5h180yaq-libmemcached-1.0.18"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/hmmmas5zp39dd7k78zlwwbmw3b1byk6d-libmemcached-1.0.18",
"default": true
}
],
"store_path": "/nix/store/hmmmas5zp39dd7k78zlwwbmw3b1byk6d-libmemcached-1.0.18"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/c0i0mfp14ligx54nljddk5arl9wdkwv3-libmemcached-1.0.18",
"default": true
}
],
"store_path": "/nix/store/c0i0mfp14ligx54nljddk5arl9wdkwv3-libmemcached-1.0.18"
}
}
},
"mailpit@latest": {
"last_modified": "2025-05-24T21:46:02Z",
"resolved": "github:NixOS/nixpkgs/edb3633f9100d9277d1c9af245a4e9337a980c07#mailpit",
"source": "devbox-search",
"version": "1.25.0",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/7ysfr69vnq6lxaxsnxg6jf3p1isxk2jn-mailpit-1.25.0",
"default": true
}
],
"store_path": "/nix/store/7ysfr69vnq6lxaxsnxg6jf3p1isxk2jn-mailpit-1.25.0"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/yh0g1hb48a9dyxdcaxvf02sjswzi7jjf-mailpit-1.25.0",
"default": true
}
],
"store_path": "/nix/store/yh0g1hb48a9dyxdcaxvf02sjswzi7jjf-mailpit-1.25.0"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/4aqg7nxmas50rk32yvpij27y5hdxn180-mailpit-1.25.0",
"default": true
}
],
"store_path": "/nix/store/4aqg7nxmas50rk32yvpij27y5hdxn180-mailpit-1.25.0"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/6wfbaj30nndwbsyc4ih9k51v22gl5f02-mailpit-1.25.0",
"default": true
}
],
"store_path": "/nix/store/6wfbaj30nndwbsyc4ih9k51v22gl5f02-mailpit-1.25.0"
}
}
},
"memcached@latest": {
"last_modified": "2025-05-16T20:19:48Z",
"resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#memcached",
"source": "devbox-search",
"version": "1.6.37",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/a137h68f4ziy8fwr8dvn2w1vqj8l360g-memcached-1.6.37",
"default": true
}
],
"store_path": "/nix/store/a137h68f4ziy8fwr8dvn2w1vqj8l360g-memcached-1.6.37"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/lh006hm4pa3iir98fx2c4b8w3x0aq8hx-memcached-1.6.37",
"default": true
}
],
"store_path": "/nix/store/lh006hm4pa3iir98fx2c4b8w3x0aq8hx-memcached-1.6.37"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/49qnlj5zwrxnhci8qqcb80jp26bds7w8-memcached-1.6.37",
"default": true
}
],
"store_path": "/nix/store/49qnlj5zwrxnhci8qqcb80jp26bds7w8-memcached-1.6.37"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/jbisa18v6lcm4wjigr2zb0c93c7ma7jc-memcached-1.6.37",
"default": true
}
],
"store_path": "/nix/store/jbisa18v6lcm4wjigr2zb0c93c7ma7jc-memcached-1.6.37"
}
}
},
"pdm@latest": {
"last_modified": "2025-05-16T20:19:48Z",
"resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#pdm",
"source": "devbox-search",
"version": "2.24.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/hh60gc19npvy9l9gmca641m8nhxpph91-pdm-2.24.1",
"default": true
},
{
"name": "dist",
"path": "/nix/store/f66gsnff3sg9d0r510kfi3p711j84hvh-pdm-2.24.1-dist"
}
],
"store_path": "/nix/store/hh60gc19npvy9l9gmca641m8nhxpph91-pdm-2.24.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/f0rnz99y06bx5qsqx66py1f3zarq6859-pdm-2.24.1",
"default": true
},
{
"name": "dist",
"path": "/nix/store/4s1ddraq0ykn91xg2aszpcnirqfb8krf-pdm-2.24.1-dist"
}
],
"store_path": "/nix/store/f0rnz99y06bx5qsqx66py1f3zarq6859-pdm-2.24.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/g7m3h8g2p9gcxf3hsdfbpzskdsijqmm5-pdm-2.24.1",
"default": true
},
{
"name": "dist",
"path": "/nix/store/wn8j6kcs631zkz6rnmpcjy5wr6rsxarw-pdm-2.24.1-dist"
}
],
"store_path": "/nix/store/g7m3h8g2p9gcxf3hsdfbpzskdsijqmm5-pdm-2.24.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/1gyv3dfxgk7wmc6vb68nphxv69p2jy3a-pdm-2.24.1",
"default": true
},
{
"name": "dist",
"path": "/nix/store/3xlmsdvha92v6aqrq51mx8yym12mlydg-pdm-2.24.1-dist"
}
],
"store_path": "/nix/store/1gyv3dfxgk7wmc6vb68nphxv69p2jy3a-pdm-2.24.1"
}
}
},
"python@3.13": {
"last_modified": "2025-05-16T20:19:48Z",
"plugin_version": "0.0.4",
"resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#python313",
"source": "devbox-search",
"version": "3.13.3",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/1a8xg8l3m67hxinxzzcsak9736qm9vsf-python3-3.13.3",
"default": true
}
],
"store_path": "/nix/store/1a8xg8l3m67hxinxzzcsak9736qm9vsf-python3-3.13.3"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/yy0xvc2rydhrs0h1v8d7r3sql347xzz5-python3-3.13.3",
"default": true
},
{
"name": "debug",
"path": "/nix/store/42bxfqfrh8cwspl7szr0cw8739xv8qlq-python3-3.13.3-debug"
}
],
"store_path": "/nix/store/yy0xvc2rydhrs0h1v8d7r3sql347xzz5-python3-3.13.3"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/gbrigjhghz9v2p0zf9b2fnvs0g0yx7q4-python3-3.13.3",
"default": true
}
],
"store_path": "/nix/store/gbrigjhghz9v2p0zf9b2fnvs0g0yx7q4-python3-3.13.3"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/2mab9iiwhcqwk75qwvp3zv0bvbiaq6cs-python3-3.13.3",
"default": true
},
{
"name": "debug",
"path": "/nix/store/9z6k8ijl2md0y2n95yprbjj4vxbfsi15-python3-3.13.3-debug"
}
],
"store_path": "/nix/store/2mab9iiwhcqwk75qwvp3zv0bvbiaq6cs-python3-3.13.3"
}
}
},
"redis@latest": {
"last_modified": "2025-05-16T20:19:48Z",
"plugin_version": "0.0.2",
"resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#redis",
"source": "devbox-search",
"version": "7.2.7",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/l036d84q9asf9bzq2xi5jby2m7wxcv0l-redis-7.2.7",
"default": true
}
],
"store_path": "/nix/store/l036d84q9asf9bzq2xi5jby2m7wxcv0l-redis-7.2.7"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/5h4108zpin8lidq5fzk661c2h71hdkyi-redis-7.2.7",
"default": true
}
],
"store_path": "/nix/store/5h4108zpin8lidq5fzk661c2h71hdkyi-redis-7.2.7"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/wh6crdasw8762d7szd17xah1312wwps0-redis-7.2.7",
"default": true
}
],
"store_path": "/nix/store/wh6crdasw8762d7szd17xah1312wwps0-redis-7.2.7"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/dfgshgy5ayrdkn4jsjmnq782rfpz7c1y-redis-7.2.7",
"default": true
}
],
"store_path": "/nix/store/dfgshgy5ayrdkn4jsjmnq782rfpz7c1y-redis-7.2.7"
}
}
},
"zlib@latest": {
"last_modified": "2025-05-26T17:43:11Z",
"resolved": "github:NixOS/nixpkgs/bdc995d3e97cec29eacc8fbe87e66edfea26b861#zlib",
"source": "devbox-search",
"version": "1.3.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/nvss2yn25aqrsri04bxj6izw2gwb65fy-zlib-1.3.1",
"default": true
},
{
"name": "static",
"path": "/nix/store/l1h4zhp9k3jdmqb1999nqjrn5dq9nw6r-zlib-1.3.1-static"
},
{
"name": "dev",
"path": "/nix/store/kslccv8lpcq68f4jfppsx4d0zxmjcmrf-zlib-1.3.1-dev"
}
],
"store_path": "/nix/store/nvss2yn25aqrsri04bxj6izw2gwb65fy-zlib-1.3.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/hiixzjv7rjawcacsldws06gskcpddviq-zlib-1.3.1",
"default": true
},
{
"name": "dev",
"path": "/nix/store/lqv3nwcq8p1x6xh2m0f5pz4ia7710iq5-zlib-1.3.1-dev"
},
{
"name": "static",
"path": "/nix/store/99h9h2z2mmy1x0f4zqc3ks8vc0039l5i-zlib-1.3.1-static"
}
],
"store_path": "/nix/store/hiixzjv7rjawcacsldws06gskcpddviq-zlib-1.3.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/qyka6wd4i9pl3qim7qkllkcj32wkd0zx-zlib-1.3.1",
"default": true
},
{
"name": "dev",
"path": "/nix/store/518l0cihhpavzhpcavy1d1p6wbkpacpx-zlib-1.3.1-dev"
},
{
"name": "static",
"path": "/nix/store/7i8cs8r87i0lmsywgbiygwslkcky2s6w-zlib-1.3.1-static"
}
],
"store_path": "/nix/store/qyka6wd4i9pl3qim7qkllkcj32wkd0zx-zlib-1.3.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/srby6wmvg7dp454pwb6qvaxdiri38sc1-zlib-1.3.1",
"default": true
},
{
"name": "static",
"path": "/nix/store/f4d4vnp0f60r85p4s72ymkmj9camqs2m-zlib-1.3.1-static"
},
{
"name": "dev",
"path": "/nix/store/cbdvjyn19y77m8l06n089x30v7irqz3j-zlib-1.3.1-dev"
}
],
"store_path": "/nix/store/srby6wmvg7dp454pwb6qvaxdiri38sc1-zlib-1.3.1"
}
}
}
}
}

View File

@ -3,7 +3,7 @@
[metadata]
groups = ["default", "alarm", "argparse", "cli", "colorlog", "configparser", "daemon", "dev", "docs", "dummy", "generate", "jinja2", "json", "logging", "memcached", "mustache", "plugin", "print", "redis", "scrub", "smtp", "tabulate", "watchdog", "yaml"]
strategy = ["cross_platform", "inherit_metadata"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:edb453570d1f7e2dcd14a71e866fa1814be1c5341a716c96164858feae2e3662"
@ -429,7 +429,7 @@ name = "jinja2"
version = "3.1.6"
requires_python = ">=3.7"
summary = "A very fast and expressive template engine."
groups = ["docs", "jinja2"]
groups = ["cli", "docs", "jinja2"]
dependencies = [
"MarkupSafe>=2.0",
]
@ -443,7 +443,7 @@ name = "markupsafe"
version = "2.1.5"
requires_python = ">=3.7"
summary = "Safely add untrusted strings to HTML/XML markup."
groups = ["docs", "jinja2"]
groups = ["cli", "docs", "jinja2"]
files = [
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
@ -718,7 +718,7 @@ name = "pyyaml"
version = "6.0.2"
requires_python = ">=3.8"
summary = "YAML parser and emitter for Python"
groups = ["generate", "yaml"]
groups = ["cli", "generate", "yaml"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@ -777,7 +777,7 @@ files = [
[[package]]
name = "redis"
version = "6.0.0"
version = "6.1.1"
requires_python = ">=3.8"
summary = "Python client for Redis database and key-value store"
groups = ["redis"]
@ -785,8 +785,8 @@ dependencies = [
"async-timeout>=4.0.3; python_full_version < \"3.11.3\"",
]
files = [
{file = "redis-6.0.0-py3-none-any.whl", hash = "sha256:a2e040aee2cdd947be1fa3a32e35a956cd839cc4c1dbbe4b2cdee5b9623fd27c"},
{file = "redis-6.0.0.tar.gz", hash = "sha256:5446780d2425b787ed89c91ddbfa1be6d32370a636c8fdb687f11b1c26c1fa88"},
{file = "redis-6.1.1-py3-none-any.whl", hash = "sha256:ed44d53d065bbe04ac6d76864e331cfe5c5353f86f6deccc095f8794fd15bb2e"},
{file = "redis-6.1.1.tar.gz", hash = "sha256:88c689325b5b41cedcbdbdfd4d937ea86cf6dab2222a83e86d8a466e4b3d2600"},
]
[[package]]
@ -808,29 +808,29 @@ files = [
[[package]]
name = "ruff"
version = "0.11.8"
version = "0.11.13"
requires_python = ">=3.7"
summary = "An extremely fast Python linter and code formatter, written in Rust."
groups = ["dev"]
files = [
{file = "ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3"},
{file = "ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835"},
{file = "ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458"},
{file = "ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5"},
{file = "ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948"},
{file = "ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb"},
{file = "ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c"},
{file = "ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304"},
{file = "ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2"},
{file = "ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4"},
{file = "ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2"},
{file = "ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8"},
{file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"},
{file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"},
{file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"},
{file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"},
{file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"},
{file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"},
{file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"},
{file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"},
{file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"},
{file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"},
{file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"},
{file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"},
]
[[package]]

19
process-compose.yml Normal file
View File

@ -0,0 +1,19 @@
# Process compose for starting django
version: "0.5"
processes:
memcached:
command: memcached
availability:
restart: "always"
mailpit:
command: mailpit
availability:
restart: "always"
environment:
- "MP_MAX_MESSAGES=5000"
- "MP_SMTP_AUTH_ACCEPT_ANY=1"
- "MP_SMTP_AUTH_ALLOW_INSECURE=1"
- "MP_SMTP_TLS_CERT=docker/mailpit/dev-cert.pem"
- "MP_SMTP_TLS_KEY=docker/mailpit/dev-key.pem"

View File

@ -77,24 +77,26 @@ def test_bogus_group(rando):
env.switch()
def _daemon_target(pid_file):
"""Target function for daemon test subprocess."""
with TestApp(argv=['--daemon'], extensions=['daemon']) as app:
app.config.set('daemon', 'pid_file', pid_file)
try:
# FIX ME: Can't daemonize, because nose/pytest lose sight of it
app.daemonize()
app.run()
finally:
app.close()
ext_daemon.cleanup(app)
def test_daemon(tmp):
os.remove(tmp.file)
from cement.utils import shell
# Test in a sub-process to avoid hangup
def target():
with TestApp(argv=['--daemon'], extensions=['daemon']) as app:
app.config.set('daemon', 'pid_file', tmp.file)
try:
# FIX ME: Can't daemonize, because nose/pytest lose sight of it
app.daemonize()
app.run()
finally:
app.close()
ext_daemon.cleanup(app)
p = shell.spawn_process(target)
p = shell.spawn_process(_daemon_target, args=(tmp.file,))
p.join()
assert p.exitcode == 0

View File

@ -21,34 +21,44 @@ class WatchdogApp(TestApp):
def test_watchdog(tmp):
# The exception is getting raised, but for some reason it's not being
# caught by a with raises() block, so I'm mocking it out instead.
# Clear any existing mock first
if hasattr(MyEventHandler, 'on_any_event'):
if hasattr(MyEventHandler.on_any_event, 'reset_mock'):
MyEventHandler.on_any_event.reset_mock()
MyEventHandler.on_any_event = Mock()
with WatchdogApp() as app:
app.watchdog.add(tmp.dir, event_handler=MyEventHandler)
app.run()
try:
with WatchdogApp() as app:
app.watchdog.add(tmp.dir, event_handler=MyEventHandler)
app.run()
file_path = fs.join(tmp.dir, 'test.file')
# trigger an event
f = open(file_path, 'w')
f.write('test data')
f.close()
time.sleep(1)
file_path = fs.join(tmp.dir, 'test.file')
# trigger an event
f = open(file_path, 'w')
f.write('test data')
f.close()
time.sleep(1)
# 5 or 6 separate calls: See print(MyEventHandler.on_any_event.mock_calls)
# 5 or 6 separate calls: See print(MyEventHandler.on_any_event.mock_calls)
# Python < 3.11
# Tmp File created
# Tmp Dir modified
# Tmp File modified
# Tmp File closed
# Tmp Dir modified
# Python < 3.11
# Tmp File created
# Tmp Dir modified
# Tmp File modified
# Tmp File closed
# Tmp Dir modified
# In Python >= 3.11 this has one additional
# Tmp File opened
# In Python >= 3.11 this has one additional
# Tmp File opened
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 5 or 6
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 3-6 depending on platform/timing
assert MyEventHandler.on_any_event.call_count in [5, 6]
assert MyEventHandler.on_any_event.call_count in [3, 4, 5, 6]
finally:
# Reset mock to avoid interfering with other tests
if (hasattr(MyEventHandler, 'on_any_event') and
hasattr(MyEventHandler.on_any_event, 'reset_mock')):
MyEventHandler.on_any_event.reset_mock()
def test_watchdog_app_paths(tmp):
@ -60,40 +70,50 @@ def test_watchdog_app_paths(tmp):
(tmp.dir, WatchdogEventHandler)
]
# Clear any existing mock first
if hasattr(WatchdogEventHandler, 'on_any_event'):
if hasattr(WatchdogEventHandler.on_any_event, 'reset_mock'):
WatchdogEventHandler.on_any_event.reset_mock()
WatchdogEventHandler.on_any_event = Mock()
with MyApp() as app:
app.run()
try:
with MyApp() as app:
app.run()
WatchdogEventHandler.on_any_event.reset_mock()
# trigger an event
f = open(fs.join(tmp.dir, 'test.file'), 'w')
f.write('test data')
f.close()
time.sleep(1)
WatchdogEventHandler.on_any_event.reset_mock()
# trigger an event
f = open(fs.join(tmp.dir, 'test.file'), 'w')
f.write('test data')
f.close()
time.sleep(1)
# 10 or 12 separate calls
# See print(MyEventHandler.on_any_event.mock_calls)
# 10 or 12 separate calls
# See print(MyEventHandler.on_any_event.mock_calls)
# Python < 3.11
# Tmp File created
# Tmp File created
# Tmp Dir modified
# Tmp Dir modified
# Tmp File modified
# Tmp File modified
# Tmp File closed
# Tmp File closed
# Tmp Dir modified
# Tmp Dir modified
# Python < 3.11
# Tmp File created
# Tmp File created
# Tmp Dir modified
# Tmp Dir modified
# Tmp File modified
# Tmp File modified
# Tmp File closed
# Tmp File closed
# Tmp Dir modified
# Tmp Dir modified
# In Python >= 3.11 this has one additional
# Tmp File opened
# Tmp File opened
# In Python >= 3.11 this has one additional
# Tmp File opened
# Tmp File opened
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 10 or 12
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 6-12 depending on platform/timing
assert WatchdogEventHandler.on_any_event.call_count in [10, 12]
assert WatchdogEventHandler.on_any_event.call_count in [6, 8, 10, 12]
finally:
# Reset mock to avoid interfering with other tests
if (hasattr(WatchdogEventHandler, 'on_any_event') and
hasattr(WatchdogEventHandler.on_any_event, 'reset_mock')):
WatchdogEventHandler.on_any_event.reset_mock()
def test_watchdog_app_paths_bad_spec(tmp):
@ -109,33 +129,43 @@ def test_watchdog_app_paths_bad_spec(tmp):
def test_watchdog_default_event_handler(tmp):
# Clear any existing mock first
if hasattr(WatchdogEventHandler, 'on_any_event'):
if hasattr(WatchdogEventHandler.on_any_event, 'reset_mock'):
WatchdogEventHandler.on_any_event.reset_mock()
WatchdogEventHandler.on_any_event = Mock()
with WatchdogApp() as app:
app.watchdog.add(tmp.dir)
app.run()
try:
with WatchdogApp() as app:
app.watchdog.add(tmp.dir)
app.run()
f = open(fs.join(tmp.dir, 'test.file'), 'w')
f.write('test data')
f.close()
time.sleep(1)
f = open(fs.join(tmp.dir, 'test.file'), 'w')
f.write('test data')
f.close()
time.sleep(1)
# 5 or 6 separate calls
# See print(MyEventHandler.on_any_event.mock_calls)
# 5 or 6 separate calls
# See print(MyEventHandler.on_any_event.mock_calls)
# Python < 3.11
# Tmp File created
# Tmp Dir modified
# Tmp File modified
# Tmp File closed
# Tmp Dir modified
# Python < 3.11
# Tmp File created
# Tmp Dir modified
# Tmp File modified
# Tmp File closed
# Tmp Dir modified
# In Python >= 3.11 this has one additional
# Tmp File opened
# In Python >= 3.11 this has one additional
# Tmp File opened
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 5 or 6
# But on Travis... this isn't resulting in the same counts so
# fudging the test a little... it's 3-6 depending on platform/timing
assert WatchdogEventHandler.on_any_event.call_count in [5, 6]
assert WatchdogEventHandler.on_any_event.call_count in [3, 4, 5, 6]
finally:
# Reset mock to avoid interfering with other tests
if (hasattr(WatchdogEventHandler, 'on_any_event') and
hasattr(WatchdogEventHandler.on_any_event, 'reset_mock')):
WatchdogEventHandler.on_any_event.reset_mock()
def test_watchdog_bad_path(tmp):