diff --git a/.alfred.toml b/.alfred.toml new file mode 100644 index 0000000..7d0dc8a --- /dev/null +++ b/.alfred.toml @@ -0,0 +1,5 @@ +[alfred] + +[alfred.project] +pythonpath_extends = ['tests'] +venv = ".venv" diff --git a/.alfred.yml b/.alfred.yml deleted file mode 100644 index 848425f..0000000 --- a/.alfred.yml +++ /dev/null @@ -1,2 +0,0 @@ -plugins: - - path: alfred diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54393c3..f8b73c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 1ba4282..7278735 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,8 @@ /dist /examples/*/build .idea + +**.pyc + +/.venv/* +!/.venv/.gitkeep diff --git a/.venv/.gitkeep b/.venv/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Pipfile b/Pipfile index e2c32b0..888887d 100644 --- a/Pipfile +++ b/Pipfile @@ -2,16 +2,16 @@ url = "https://pypi.org/simple" verify_ssl = true name = "pypi" - # pipenv install + [packages] fixtup = {path = ".", editable=true} six = "*" - # pipenv install --dev + [dev-packages] fixtup = {path = ".", extras = ["dev"], editable=true} - # pipenv install --categories="dev_windows" + [dev_windows] fixtup = {path = ".", extras = ["dev_windows"], editable=true} diff --git a/Pipfile.lock b/Pipfile.lock index 362d31d..58f7ce4 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -16,19 +16,19 @@ "default": { "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.1.0" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "charset-normalizer": { "hashes": [ @@ -141,67 +141,75 @@ }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.3" + }, + "platformdirs": { + "hashes": [ + "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc", + "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e" ], "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "version": "==3.8.0" }, "plumbum": { "hashes": [ - "sha256:07cf5f50bf739e91fb83ce304fc66b41dbd12db4d4546ff5266087dd9d148314", - "sha256:88a40fc69247d0cd585e21ca169b3820f46c484535102e16455d2202727bb37b" + "sha256:3ad9e5f56c6ec98f6f7988f7ea8b52159662ea9e915868d369dbccbfca0e367e", + "sha256:9e6dc032f4af952665f32f3206567bc23b7858b1413611afe603a3f8ad9bfd75" ], "markers": "python_version >= '3.6'", - "version": "==1.8.1" + "version": "==1.8.2" }, "prompt-toolkit": { "hashes": [ @@ -267,11 +275,11 @@ }, "requests": { "hashes": [ - "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", - "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.28.2" + "markers": "python_version >= '3.7'", + "version": "==2.31.0" }, "six": { "hashes": [ @@ -291,11 +299,11 @@ }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1", + "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "markers": "python_version >= '3.7'", + "version": "==2.0.3" }, "wcwidth": { "hashes": [ @@ -316,26 +324,26 @@ }, "alfred-cli": { "hashes": [ - "sha256:248a40ef41d2cb8b91568fa3ace21d11c506a3869fa5eacb01176172710cad72", - "sha256:a629924bbcf9ad8a420eb0aa16d34308f2f7d1bbdea508afde273e2a851efd9f" + "sha256:0761655569e0ed887b1c4985bcf4006d9ff36b2588a0d74656988ee4d0f68632", + "sha256:7836c8f026f2f5f5198d32edbaa6aa654f1cf12a503e856a0924d6d1519fcc7f" ], - "markers": "python_version >= '3.6'", - "version": "==1.2.0" + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==2.1.0" }, "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.1.0" }, "babel": { "hashes": [ "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610", "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==2.12.1" }, "bleach": { @@ -343,7 +351,7 @@ "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414", "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==6.0.0" }, "build": { @@ -351,16 +359,16 @@ "sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171", "sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==0.10.0" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cffi": { "hashes": [ @@ -522,28 +530,28 @@ }, "cryptography": { "hashes": [ - "sha256:0a4e3406cfed6b1f6d6e87ed243363652b2586b2d917b0609ca4f97072994405", - "sha256:1e0af458515d5e4028aad75f3bb3fe7a31e46ad920648cd59b64d3da842e4356", - "sha256:2803f2f8b1e95f614419926c7e6f55d828afc614ca5ed61543877ae668cc3472", - "sha256:28d63d75bf7ae4045b10de5413fb1d6338616e79015999ad9cf6fc538f772d41", - "sha256:32057d3d0ab7d4453778367ca43e99ddb711770477c4f072a51b3ca69602780a", - "sha256:3a4805a4ca729d65570a1b7cac84eac1e431085d40387b7d3bbaa47e39890b88", - "sha256:63dac2d25c47f12a7b8aa60e528bfb3c51c5a6c5a9f7c86987909c6c79765554", - "sha256:650883cc064297ef3676b1db1b7b1df6081794c4ada96fa457253c4cc40f97db", - "sha256:6f2bbd72f717ce33100e6467572abaedc61f1acb87b8d546001328d7f466b778", - "sha256:7c872413353c70e0263a9368c4993710070e70ab3e5318d85510cc91cce77e7c", - "sha256:918cb89086c7d98b1b86b9fdb70c712e5a9325ba6f7d7cfb509e784e0cfc6917", - "sha256:9618a87212cb5200500e304e43691111570e1f10ec3f35569fdfcd17e28fd797", - "sha256:a805a7bce4a77d51696410005b3e85ae2839bad9aa38894afc0aa99d8e0c3160", - "sha256:cc3a621076d824d75ab1e1e530e66e7e8564e357dd723f2533225d40fe35c60c", - "sha256:cd033d74067d8928ef00a6b1327c8ea0452523967ca4463666eeba65ca350d4c", - "sha256:cf91e428c51ef692b82ce786583e214f58392399cf65c341bc7301d096fa3ba2", - "sha256:d36bbeb99704aabefdca5aee4eba04455d7a27ceabd16f3b3ba9bdcc31da86c4", - "sha256:d8aa3609d337ad85e4eb9bb0f8bcf6e4409bfb86e706efa9a027912169e89122", - "sha256:f5d7b79fa56bc29580faafc2ff736ce05ba31feaa9d4735048b0de7d9ceb2b94" + "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db", + "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a", + "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039", + "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c", + "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3", + "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485", + "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c", + "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca", + "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5", + "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5", + "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3", + "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb", + "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43", + "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31", + "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc", + "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b", + "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006", + "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a", + "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699" ], - "markers": "python_version >= '3.6'", - "version": "==40.0.1" + "markers": "python_version >= '3.7'", + "version": "==41.0.1" }, "docutils": { "hashes": [ @@ -575,18 +583,18 @@ }, "importlib-metadata": { "hashes": [ - "sha256:43ce9281e097583d758c2c708c4376371261a02c34682491a8e98352365aad20", - "sha256:ff80f3b5394912eb1b108fcfd444dc78b7f1f3e16b16188054bd01cb9cb86f09" + "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", + "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==6.1.0" + "markers": "python_version < '3.10'", + "version": "==6.7.0" }, "jaraco.classes": { "hashes": [ "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158", "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==3.2.3" }, "jeepney": { @@ -607,82 +615,82 @@ }, "keyring": { "hashes": [ - "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd", - "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678" + "sha256:ade5e1e7710a7579d7c01e64a712926270239aba48055b1cdc6c022dd6d789b5", + "sha256:bd48a36612ef55505bf70e563528e3e66ba93267e344b6780cf6151f9c1eda6d" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==23.13.1" + "markers": "python_version >= '3.7'", + "version": "==24.1.0" }, "markdown-it-py": { "hashes": [ - "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30", - "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1" + "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==2.2.0" + "markers": "python_version >= '3.8'", + "version": "==3.0.0" }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" ], "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "version": "==2.1.3" }, "mdurl": { "hashes": [ "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==0.1.2" }, "more-itertools": { @@ -690,40 +698,40 @@ "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d", "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==9.1.0" }, "mypy": { "hashes": [ - "sha256:0a28a76785bf57655a8ea5eb0540a15b0e781c807b5aa798bd463779988fa1d5", - "sha256:19ba15f9627a5723e522d007fe708007bae52b93faab00f95d72f03e1afa9598", - "sha256:21b437be1c02712a605591e1ed1d858aba681757a1e55fe678a15c2244cd68a5", - "sha256:26cdd6a22b9b40b2fd71881a8a4f34b4d7914c679f154f43385ca878a8297389", - "sha256:2888ce4fe5aae5a673386fa232473014056967f3904f5abfcf6367b5af1f612a", - "sha256:2b0c373d071593deefbcdd87ec8db91ea13bd8f1328d44947e88beae21e8d5e9", - "sha256:315ac73cc1cce4771c27d426b7ea558fb4e2836f89cb0296cbe056894e3a1f78", - "sha256:39c7119335be05630611ee798cc982623b9e8f0cff04a0b48dfc26100e0b97af", - "sha256:4b398d8b1f4fba0e3c6463e02f8ad3346f71956b92287af22c9b12c3ec965a9f", - "sha256:4e4e8b362cdf99ba00c2b218036002bdcdf1e0de085cdb296a49df03fb31dfc4", - "sha256:59bbd71e5c58eed2e992ce6523180e03c221dcd92b52f0e792f291d67b15a71c", - "sha256:5b5f81b40d94c785f288948c16e1f2da37203c6006546c5d947aab6f90aefef2", - "sha256:5cb14ff9919b7df3538590fc4d4c49a0f84392237cbf5f7a816b4161c061829e", - "sha256:61bf08362e93b6b12fad3eab68c4ea903a077b87c90ac06c11e3d7a09b56b9c1", - "sha256:64cc3afb3e9e71a79d06e3ed24bb508a6d66f782aff7e56f628bf35ba2e0ba51", - "sha256:69b35d1dcb5707382810765ed34da9db47e7f95b3528334a3c999b0c90fe523f", - "sha256:9401e33814cec6aec8c03a9548e9385e0e228fc1b8b0a37b9ea21038e64cdd8a", - "sha256:a380c041db500e1410bb5b16b3c1c35e61e773a5c3517926b81dfdab7582be54", - "sha256:ae9ceae0f5b9059f33dbc62dea087e942c0ccab4b7a003719cb70f9b8abfa32f", - "sha256:b7c7b708fe9a871a96626d61912e3f4ddd365bf7f39128362bc50cbd74a634d5", - "sha256:c1c10fa12df1232c936830839e2e935d090fc9ee315744ac33b8a32216b93707", - "sha256:ce61663faf7a8e5ec6f456857bfbcec2901fbdb3ad958b778403f63b9e606a1b", - "sha256:d64c28e03ce40d5303450f547e07418c64c241669ab20610f273c9e6290b4b0b", - "sha256:d809f88734f44a0d44959d795b1e6f64b2bbe0ea4d9cc4776aa588bb4229fc1c", - "sha256:dbb19c9f662e41e474e0cff502b7064a7edc6764f5262b6cd91d698163196799", - "sha256:ef6a01e563ec6a4940784c574d33f6ac1943864634517984471642908b30b6f7" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==1.1.1" + "sha256:0cf0ca95e4b8adeaf07815a78b4096b65adf64ea7871b39a2116c19497fcd0dd", + "sha256:0f98973e39e4a98709546a9afd82e1ffcc50c6ec9ce6f7870f33ebbf0bd4f26d", + "sha256:19d42b08c7532d736a7e0fb29525855e355fa51fd6aef4f9bbc80749ff64b1a2", + "sha256:210fe0f39ec5be45dd9d0de253cb79245f0a6f27631d62e0c9c7988be7152965", + "sha256:3b1b5c875fcf3e7217a3de7f708166f641ca154b589664c44a6fd6d9f17d9e7e", + "sha256:3f2b353eebef669529d9bd5ae3566905a685ae98b3af3aad7476d0d519714758", + "sha256:50f65f0e9985f1e50040e603baebab83efed9eb37e15a22a4246fa7cd660f981", + "sha256:53c2a1fed81e05ded10a4557fe12bae05b9ecf9153f162c662a71d924d504135", + "sha256:5a0ee54c2cb0f957f8a6f41794d68f1a7e32b9968675ade5846f538504856d42", + "sha256:62bf18d97c6b089f77f0067b4e321db089d8520cdeefc6ae3ec0f873621c22e5", + "sha256:653863c75f0dbb687d92eb0d4bd9fe7047d096987ecac93bb7b1bc336de48ebd", + "sha256:67242d5b28ed0fa88edd8f880aed24da481929467fdbca6487167cb5e3fd31ff", + "sha256:6ba9a69172abaa73910643744d3848877d6aac4a20c41742027dcfd8d78f05d9", + "sha256:6c34d43e3d54ad05024576aef28081d9d0580f6fa7f131255f54020eb12f5352", + "sha256:7461469e163f87a087a5e7aa224102a30f037c11a096a0ceeb721cb0dce274c8", + "sha256:94a81b9354545123feb1a99b960faeff9e1fa204fce47e0042335b473d71530d", + "sha256:a0b2e0da7ff9dd8d2066d093d35a169305fc4e38db378281fce096768a3dbdbf", + "sha256:a34eed094c16cad0f6b0d889811592c7a9b7acf10d10a7356349e325d8704b4f", + "sha256:a3af348e0925a59213244f28c7c0c3a2c2088b4ba2fe9d6c8d4fbb0aba0b7d05", + "sha256:b4c734d947e761c7ceb1f09a98359dd5666460acbc39f7d0a6b6beec373c5840", + "sha256:bba57b4d2328740749f676807fcf3036e9de723530781405cc5a5e41fc6e20de", + "sha256:ca33ab70a4aaa75bb01086a0b04f0ba8441e51e06fc57e28585176b08cad533b", + "sha256:de1e7e68148a213036276d1f5303b3836ad9a774188961eb2684eddff593b042", + "sha256:f051ca656be0c179c735a4c3193f307d34c92fdc4908d44fd4516fbe8b10567d", + "sha256:f5984a8d13d35624e3b235a793c814433d810acba9eeefe665cdfed3d08bc3af", + "sha256:f7a5971490fd4a5a436e143105a1f78fa8b3fe95b30fff2a77542b4f3227a01f" + ], + "markers": "python_version >= '3.7'", + "version": "==1.4.0" }, "mypy-extensions": { "hashes": [ @@ -735,11 +743,11 @@ }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==23.0" + "markers": "python_version >= '3.7'", + "version": "==23.1" }, "pkginfo": { "hashes": [ @@ -749,13 +757,21 @@ "markers": "python_version >= '3.6'", "version": "==1.9.6" }, + "platformdirs": { + "hashes": [ + "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc", + "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.0" + }, "plumbum": { "hashes": [ - "sha256:07cf5f50bf739e91fb83ce304fc66b41dbd12db4d4546ff5266087dd9d148314", - "sha256:88a40fc69247d0cd585e21ca169b3820f46c484535102e16455d2202727bb37b" + "sha256:3ad9e5f56c6ec98f6f7988f7ea8b52159662ea9e915868d369dbccbfca0e367e", + "sha256:9e6dc032f4af952665f32f3206567bc23b7858b1413611afe603a3f8ad9bfd75" ], "markers": "python_version >= '3.6'", - "version": "==1.8.1" + "version": "==1.8.2" }, "prompt-toolkit": { "hashes": [ @@ -774,18 +790,18 @@ }, "pygments": { "hashes": [ - "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297", - "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717" + "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", + "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" ], - "markers": "python_version >= '3.6'", - "version": "==2.14.0" + "markers": "python_version >= '3.7'", + "version": "==2.15.1" }, "pyproject-hooks": { "hashes": [ "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8", "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==1.0.0" }, "python-dotenv": { @@ -844,43 +860,43 @@ }, "readme-renderer": { "hashes": [ - "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273", - "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343" + "sha256:9f77b519d96d03d7d7dce44977ba543090a14397c4f60de5b6eb5b8048110aa4", + "sha256:e18feb2a1e7706f2865b81ebb460056d93fb29d69daa10b223c00faa7bd9a00a" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==37.3" + "markers": "python_version >= '3.8'", + "version": "==40.0" }, "requests": { "hashes": [ - "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", - "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.28.2" + "markers": "python_version >= '3.7'", + "version": "==2.31.0" }, "requests-toolbelt": { "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" + "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", + "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" + "version": "==1.0.0" }, "rfc3986": { "hashes": [ "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==2.0.0" }, "rich": { "hashes": [ - "sha256:91954fe80cfb7985727a467ca98a7618e5dd15178cc2da10f553b36a93859001", - "sha256:a104f37270bf677148d8acb07d33be1569eeee87e2d1beb286a4e9113caf6f2f" + "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec", + "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==13.3.2" + "markers": "python_version >= '3.7'", + "version": "==13.4.2" }, "secretstorage": { "hashes": [ @@ -907,19 +923,19 @@ }, "sphinx": { "hashes": [ - "sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2", - "sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc" + "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b", + "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912" ], "markers": "python_version >= '3.8'", - "version": "==6.1.3" + "version": "==6.2.1" }, "sphinx-rtd-theme": { "hashes": [ - "sha256:a0d8bd1a2ed52e0b338cbe19c4b2eef3c5e7a048769753dac6a9f059c7b641b8", - "sha256:f823f7e71890abe0ac6aaa6013361ea2696fc8d3e1fa798f463e82bdb77eeff2" + "sha256:01c5c5a72e2d025bd23d1f06c59a4831b06e6ce6c01fdd5ebfe9986c0a880fc7", + "sha256:6a7e7d8af34eb8fc57d52a09c6b6b9c46ff44aea5951bc831eeb9245378f3689" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.2.0" + "version": "==1.2.2" }, "sphinxcontrib-applehelp": { "hashes": [ @@ -950,7 +966,7 @@ "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae" ], - "markers": "python_version >= '3.1'", + "markers": "python_version >= '2.7'", "version": "==4.1" }, "sphinxcontrib-jsmath": { @@ -998,52 +1014,52 @@ "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8", "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==4.0.2" }, "types-pyyaml": { "hashes": [ - "sha256:19304869a89d49af00be681e7b267414df213f4eb89634c4495fa62e8f942b9f", - "sha256:5314a4b2580999b2ea06b2e5f9a7763d860d6e09cdf21c0e9561daa9cbd60178" + "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f", + "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97" ], - "version": "==6.0.12.8" + "version": "==6.0.12.10" }, "types-requests": { "hashes": [ - "sha256:9d4002056df7ebc4ec1f28fd701fba82c5c22549c4477116cb2656aa30ace6db", - "sha256:a86921028335fdcc3aaf676c9d3463f867db6af2303fc65aa309b13ae1e6dd53" + "sha256:3de667cffa123ce698591de0ad7db034a5317457a596eb0b4944e5a9d9e8d1ac", + "sha256:afb06ef8f25ba83d59a1d424bd7a5a939082f94b94e90ab5e6116bd2559deaa3" ], - "version": "==2.28.11.16" + "version": "==2.31.0.1" }, "types-toml": { "hashes": [ - "sha256:2432017febe43174af0f3c65f03116e3d3cf43e7e1406b8200e106da8cf98992", - "sha256:bf80fce7d2d74be91148f47b88d9ae5adeb1024abef22aa2fdbabc036d6b8b3c" + "sha256:6d3ac79e36c9ee593c5d4fb33a50cca0e3adceb6ef5cff8b8e5aef67b4c4aaf2", + "sha256:de7b2bb1831d6f7a4b554671ffe5875e729753496961b3e9b202745e4955dafa" ], - "version": "==0.10.8.5" + "version": "==0.10.8.6" }, "types-urllib3": { "hashes": [ - "sha256:160727879bdbe52f11f5feeca092a473f38d68ed3be88abb461b59cda40fb9bc", - "sha256:b327d360ba4a9edd80ea82f5990ba19e76175a20b5b64be4b4813d9a1c424caa" + "sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5", + "sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c" ], - "version": "==1.26.25.9" + "version": "==1.26.25.13" }, "typing-extensions": { "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" + "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", + "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==4.5.0" + "markers": "python_version >= '3.7'", + "version": "==4.6.3" }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1", + "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "markers": "python_version >= '3.7'", + "version": "==2.0.3" }, "wcwidth": { "hashes": [ @@ -1064,7 +1080,7 @@ "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==3.15.0" } } diff --git a/alfred/publish.py b/alfred/publish.py index f4a5704..3e2021e 100644 --- a/alfred/publish.py +++ b/alfred/publish.py @@ -8,11 +8,7 @@ from click import UsageError, Choice from plumbum.commands.processes import ProcessExecutionError -import fixtup - ROOT_DIR = os.path.realpath(os.path.join(__file__, "..", "..")) -VERSION = importlib.metadata.version(fixtup.__name__) - @alfred.command("publish", help="tag a new release and trigger pypi publication") def publish(): @@ -21,6 +17,9 @@ def publish(): >>> $ alfred publish """ + import fixtup + VERSION = importlib.metadata.version(fixtup.__name__) + git = alfred.sh("git", "git should be present") os.chdir(ROOT_DIR) diff --git a/setup.cfg b/setup.cfg index 7d894c4..2eee64c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,6 +36,7 @@ install_requires = attrs click jinja2 + platformdirs prompt_toolkit plumbum pyyaml @@ -43,6 +44,7 @@ install_requires = requests toml + [options.package_data] fixtup=**/resource/**/* fixtup_typing=src/fixtup/py.typed diff --git a/src/fixtup/cli/base.py b/src/fixtup/cli/base.py index 9ab29a8..dde4cf3 100644 --- a/src/fixtup/cli/base.py +++ b/src/fixtup/cli/base.py @@ -6,7 +6,6 @@ from fixtup.cli.commands.info import info from fixtup.cli.commands.init import init from fixtup.cli.commands.new import new -from fixtup.logger import get_logger BASE_DIR = os.path.realpath(os.path.join(__file__, '..')) diff --git a/src/fixtup/cli/commands/info.py b/src/fixtup/cli/commands/info.py index e0536d5..7998595 100644 --- a/src/fixtup/cli/commands/info.py +++ b/src/fixtup/cli/commands/info.py @@ -1,18 +1,21 @@ import click from click.exceptions import ClickException +from fixtup.context import lib_context, lib_context_setup from fixtup.exceptions import FixtupException -from fixtup.settings.base import read_settings +from fixtup.settings.base import load_settings @click.command(help="Display fixtup configuration information") def info(): try: - settings = read_settings() - click.echo(f"Configuration: {settings.manifest_path}") - click.echo(f"Fixtures: {settings.fixtures_dir}") + lib_context_setup() + load_settings() + fixtup_context = lib_context() + click.echo(f"Configuration: {fixtup_context.manifestpath}") + click.echo(f"Fixtures: {fixtup_context.fixturesdir}") click.echo(f"Plugins:") - for plugin in settings.plugins: + for plugin in fixtup_context.plugins: click.echo(f" * {plugin}") exit(0) diff --git a/src/fixtup/cli/commands/new.py b/src/fixtup/cli/commands/new.py index 0ebccbe..4c67ee8 100644 --- a/src/fixtup/cli/commands/new.py +++ b/src/fixtup/cli/commands/new.py @@ -1,11 +1,10 @@ -import os - import click +from fixtup.context import lib_context_setup, lib_context from fixtup.entity.fixture_template import FixtureTemplate from fixtup.prompt.factory import lookup_prompt from fixtup.scaffold.base import scaffold_new_fixture -from fixtup.settings import read_settings +from fixtup.settings.base import load_settings @click.command(help="Scaffold a new fixture") @@ -13,13 +12,15 @@ def new() -> None: """ scaffolding command that will generate a new fixture """ - settings = read_settings() + lib_context_setup() + load_settings() + prompt = lookup_prompt() - fixture_id = prompt.new_fixture(settings.fixtures_dir) + fixtup_context = lib_context() - # output - template = FixtureTemplate.create_from_cli(fixture_id, - settings.fixtures_dir) + fixture_id = prompt.new_fixture(fixtup_context.fixturesdir) # type: ignore + # output + template = FixtureTemplate.create_from_cli(fixture_id, fixtup_context.fixturesdir) # type: ignore scaffold_new_fixture(template) diff --git a/src/fixtup/context.py b/src/fixtup/context.py new file mode 100644 index 0000000..5b5ce31 --- /dev/null +++ b/src/fixtup/context.py @@ -0,0 +1,66 @@ +""" +lib_context is a module that manages the global execution context of fixtup. This context is a source of truth shared between all fixtup modules. + +It can be retrieved with the ``lib_context()`` function. + +>>> from fixtup.context import lib_context +>>> fixtup = lib_context() +""" +from typing import Optional + +from fixtup import logger +from fixtup.entity.fixtup import Fixtup + +_lib_context: Optional[Fixtup] = None +_fake_lib_context: Optional[Fixtup] = None + +def lib_context_setup() -> None: + """ + Initialize the fixtup context. + """ + global _lib_context + if _lib_context is None: + logger.debug('init a global context for fixtup') + if _fake_lib_context is None: + _lib_context = Fixtup() + else: + _lib_context = _fake_lib_context + else: + logger.debug(f'a global context already exist for fixtup, it reuse it') + +def lib_context_teardown() -> None: + """ + Uninitialize the fixtup context. + """ + global _lib_context + _lib_context = None + + +def lib_context() -> Fixtup: + """ + returns the context of the fixtup library + """ + if _lib_context is None: + """ + If you need to emulate fixtup in automatic testing, you should prepare your test with `with context.use_fake_lib_context()` + """ + raise RuntimeError("This code must be executed in fixtup lib context.") + + return _lib_context + +def lib_context_inject() -> Fixtup: + """ + Inject a fixtup context. This is useful for testing. + """ + global _fake_lib_context + _fake_lib_context = Fixtup() + return _fake_lib_context + +def lib_context_eject() -> None: + """ + Eject the injected fixtup context. This is useful for testing. + + When tearing down a test, this method cleans up the context that would be loaded when calling `lib_context_setup()`. + """ + global _fake_lib_context + _fake_lib_context = None diff --git a/src/fixtup/entity/fixtup.py b/src/fixtup/entity/fixtup.py new file mode 100644 index 0000000..42686e0 --- /dev/null +++ b/src/fixtup/entity/fixtup.py @@ -0,0 +1,52 @@ +import typing + +import os +import tempfile +from typing import Optional, List + +import attr +import platformdirs + +if typing.TYPE_CHECKING: + from fixtup.fixture.base import FixtureEngine + + +class Driver: + mock = 'mock' + prompt_toolkit = 'prompt-toolkit' + + +@attr.s +class Fixtup: + + # Global settings + appname: str = attr.ib(default='fixtup') + appdir: Optional[str] = attr.ib(default=None) + mountdir: Optional[str] = attr.ib(default=None) + pid_owner: Optional[int] = attr.ib(default=None) + + driver_prompt: str = Driver.prompt_toolkit + enable_hooks: bool = attr.ib(default=True) + enable_plugins: bool = attr.ib(default=True) + + # Each time fixtup.up is invoked, fixtup restarts as if it were a new test run. This mode is necessary to test some workflows in the automatic tests which requires a completely blank fixtup context + emulate_new_process: bool = attr.ib(default=False) + + # Project settings + projectloaded: bool = attr.ib(default=False) + projectdir: Optional[str] = attr.ib(default=None) + manifestpath: Optional[str] = attr.ib(default=None) + fixturesdir: Optional[str] = attr.ib(default=None) + plugins: List[str] = attr.ib(factory=list) + + # Runtime settings + engine: Optional['FixtureEngine'] = attr.ib(default=None) + + def __attrs_post_init__(self): + if self.appdir is None: + self.appdir = platformdirs.user_data_dir(self.appname, self.appname) + + if self.mountdir is None: + self.mountdir = tempfile.gettempdir() + + self.pid_owner = os.getpid() diff --git a/src/fixtup/entity/fixture_template.py b/src/fixtup/entity/fixture_template.py index 06f7a14..8d2edb9 100644 --- a/src/fixtup/entity/fixture_template.py +++ b/src/fixtup/entity/fixture_template.py @@ -2,9 +2,8 @@ import attr -from fixtup.logger import get_logger +from fixtup import logger -logger = get_logger() @attr.s class FixtureTemplate(): diff --git a/src/fixtup/entity/settings.py b/src/fixtup/entity/settings.py index 96fcd40..0afa403 100644 --- a/src/fixtup/entity/settings.py +++ b/src/fixtup/entity/settings.py @@ -103,14 +103,14 @@ def default_settings_for_init(cls, manifest: ProjectManifest, fixture_repository ) @property - def configuration_dir(self) -> Optional[str]: + def projectdir(self) -> Optional[str]: if self.manifest is None: return None return os.path.dirname(self.manifest.path) @property - def manifest_path(self) -> Optional[str]: + def manifestpath(self) -> Optional[str]: if self.manifest is not None: return self.manifest.path @@ -131,8 +131,8 @@ def fixtures_dir(self): :return: absolute path of the directory that contains the fixtures """ - if self.configuration_dir is None: + if self.projectdir is None: return self.fixtures else: - return os.path.join(self.configuration_dir, self.fixtures) + return os.path.join(self.projectdir, self.fixtures) diff --git a/src/fixtup/factory.py b/src/fixtup/factory.py deleted file mode 100644 index 4f38f37..0000000 --- a/src/fixtup/factory.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -This module implements the inversion of control mechanism. - -It allows to instantiate factory methods -which take advantage of the runtime configuration to infer -the dependencies to be instantiated. - -If the code is used in unit tests, it is possible -instantiate specific dependencies. -""" -import threading -from typing import Optional, Callable, TypeVar, Any - -import attr - -T = TypeVar('T') - - -@attr.s -class RuntimeContext: - """ - The runtime context describes the conditions of the runtime is executed. - By default, RuntimeContext match production context - """ - unittest = attr.ib(default=False) - - """ - The plugins are ignored when enable_plugins is False. - - They are not loaded, neither executed. The plugin engine is doing - pass-through - """ - enable_plugins = attr.ib(default=True) - - """ -<<<<<<< HEAD - The hooks are ignored when enable_hooks is False. - - They are not loaded, neither executed. The plugin engine is doing - pass-through - """ - enable_hooks = attr.ib(default=True) - - """ - When we use 2 successive fixtures in a test and this flag is active, - we consider that the 2 fixtures are executed in separate test processes. - - This flag is essential internally to test the behavior of policies such as keep_up, ... - """ - emulate_new_process = attr.ib(default=False) - - -""" -Using threading store allow to perform dependency injection in -a multithreading context. - -Automatic test may be run in parallel, we want avoid concurrent test -share a same runtime context -""" -thread_store = threading.local() -thread_store.runtime_conf = RuntimeContext() - - -def reset_runtime_context(context: Optional[RuntimeContext] = None): - if context is None: - thread_store.runtime_conf = RuntimeContext() - else: - thread_store.runtime_conf = context - - -def depends(func: Callable[..., T], *args, **kwargs) -> T: - """ - this method allow to manage binding rules to tune the behavior depending of runtime option. - If we execute a code during unittest, we want to inject specific dependency - - :param func: - - >>> def lookup_parsers(context: RuntimeContext) -> str: - >>> if context.unittest: - >>> return "parser a" - >>> else: - >>> return "parser b" - >>> - >>> parser = depends(lookup_parsers) - >>> print(parser) - """ - - return func(thread_store.runtime_conf, *args, **kwargs) - - -def factory(func: Callable[['RuntimeContext'], T]) -> Callable[..., T]: - """ - build factory method. The runtime context will be injected from the thread store. - - >>> @factory - >>> def lookup_parsers(context: RuntimeContext) -> str: - >>> if context.unittest: - >>> return "parser a" - >>> else: - >>> return "parser b" - >>> - >>> parser = lookup_parsers() - >>> print(parser) - - :param func: - """ - - def _wrapper(*args, **kwargs) -> T: - return depends(func, *args, **kwargs) - - return _wrapper diff --git a/src/fixtup/fixtup.py b/src/fixtup/fixtup.py index c01033a..50f2c51 100644 --- a/src/fixtup/fixtup.py +++ b/src/fixtup/fixtup.py @@ -1,21 +1,20 @@ import os from contextlib import contextmanager -from typing import Generator, List, Union +from typing import Generator, Optional +from fixtup.context import lib_context_setup, lib_context_teardown from fixtup.entity.settings import Settings from fixtup.fixture.factory import lookup_fixture_engine from fixtup.fixture_template.base import fixture_template -from fixtup.logger import get_logger +from fixtup.settings.base import load_settings from fixtup.settings.module import configure_from_code -logger = get_logger() - current_working_dir = None def configure(settings: dict) -> None: """ - configure the module fixtup to override configuration + (deprecated) configure the module fixtup to override configuration You should prefer configure fixtup using python manifest like setup.cfg or pyproject.toml. @@ -34,7 +33,7 @@ def configure(settings: dict) -> None: @contextmanager -def up(fixture: str, keep_mounted_fixture: bool = False) -> Generator[None, None, None]: +def up(fixture: str, keep_mounted_fixture: bool = False, settings: Optional[dict] = None) -> Generator[None, None, None]: """ Mount a fixture to use it in a test. @@ -58,7 +57,12 @@ def up(fixture: str, keep_mounted_fixture: bool = False) -> Generator[None, None # >>> with fixtup.up('fixture1'): # >>> with fixtup.up('fixture2'): # ... - # + lib_context_setup() + if settings is not None: + _settings = Settings.from_configuration(settings) + configure_from_code(_settings) + + global current_working_dir if current_working_dir is None: highest_context = True @@ -68,6 +72,7 @@ def up(fixture: str, keep_mounted_fixture: bool = False) -> Generator[None, None if current_working_dir is not None: os.chdir(current_working_dir) + load_settings() fixture_engine = lookup_fixture_engine(highest_context=highest_context) template = fixture_template(fixture) @@ -81,3 +86,6 @@ def up(fixture: str, keep_mounted_fixture: bool = False) -> Generator[None, None os.chdir(current_working_dir) if highest_context is True: current_working_dir = None + + lib_context_teardown() + diff --git a/src/fixtup/fixture/base.py b/src/fixtup/fixture/base.py index 8146d4d..b2cc59c 100644 --- a/src/fixtup/fixture/base.py +++ b/src/fixtup/fixture/base.py @@ -8,6 +8,7 @@ import attr +from fixtup import logger from fixtup.entity.fixtup_process import FixtupProcess from fixtup.entity.fixture import Fixture, State from fixtup.entity.fixture_template import FixtureTemplate @@ -15,7 +16,6 @@ from fixtup.hook.base import HookEngine, HookEvent from fixtup.lib.env import with_cwd from fixtup.lib.signal import register_signal_handler, unregister_signal_handler -from fixtup.logger import get_logger from fixtup.os import is_posix, is_windows from fixtup.plugin.base import PluginEngine, PluginEvent @@ -50,7 +50,6 @@ def process_teardown_exit(self): self._teardown() def process_teardown_signal(self, code: int, frame: Any): - logger = get_logger() logger.info(f'user interruption through os signal {code}, tear down mounted fixtures') self._teardown() @@ -71,7 +70,6 @@ def run(self, template: FixtureTemplate, keep_mounted_fixture: bool = False): :param keep_mounted_fixture: :return: """ - logger = get_logger() fixture = self.new_fixture(template) try: self.start(template, fixture) diff --git a/src/fixtup/fixture/factory.py b/src/fixtup/fixture/factory.py index 8122e6d..d6c1d4f 100644 --- a/src/fixtup/fixture/factory.py +++ b/src/fixtup/fixture/factory.py @@ -1,45 +1,37 @@ -from typing import Optional - +from fixtup.context import lib_context from fixtup.entity.fixtup_process import fixtup_process, FixtupProcess -from fixtup.factory import factory, RuntimeContext from fixtup.fixture.base import FixtureEngine from fixtup.hook.factory import lookup_hook_engine from fixtup.plugin.factory import lookup_plugin_engine -fixture_engine: Optional[FixtureEngine] = None - -@factory -def lookup_fixture_engine(context: RuntimeContext, highest_context: bool = False) -> FixtureEngine: - """ - In the case where a fixture is invoked inside another fixture, - the existing fixture engine is reused in a test even if the emulate_new_process flag is active. - """ - global fixture_engine - if fixture_engine is not None and highest_context is False: - return fixture_engine - if fixture_engine is not None and not context.emulate_new_process: - return fixture_engine - elif fixture_engine is not None and context.emulate_new_process: - fixture_engine.unregister_process_teardown() - fixture_engine.process_teardown_exit() - fixture_engine = None +def lookup_fixture_engine(highest_context: bool = False) -> FixtureEngine: + fixtup_context = lib_context() + if fixtup_context.engine is not None and highest_context is False: + return fixtup_context.engine + if fixtup_context.engine is not None and not fixtup_context.emulate_new_process: + return fixtup_context.engine + elif fixtup_context.engine is not None and fixtup_context.emulate_new_process: + fixtup_context.engine.unregister_process_teardown() + fixtup_context.engine.process_teardown_exit() + fixtup_context.engine = None plugin_engine = lookup_plugin_engine() hook_engine = lookup_hook_engine() - # In automatic testing of fixtup itself, we need - # to ensure the fixtup process is not shared for + # In automatic testing of fixtup itself, we need to ensure the fixtup process is not shared for # some test. # - # When the runtime context requires to emulate - # a new test runtime process, we set a brand new + # When the runtime context requires to emulate a new test runtime process, we set a brand new # FixtupProcess - if context.emulate_new_process: + if fixtup_context.emulate_new_process: _fixtup_process = FixtupProcess() else: _fixtup_process = fixtup_process - fixture_engine = FixtureEngine(hook_engine, plugin_engine, _fixtup_process) return fixture_engine + +def fixture_engine_down() -> None: + global fixture_engine + fixture_engine = None diff --git a/src/fixtup/fixture_template/base.py b/src/fixtup/fixture_template/base.py index 779721c..9e8ce38 100644 --- a/src/fixtup/fixture_template/base.py +++ b/src/fixtup/fixture_template/base.py @@ -3,15 +3,16 @@ import yaml +from fixtup.context import lib_context from fixtup.entity.fixture_template import FixtureTemplate from fixtup.exceptions import FixtureNotFound from fixtup.logger import get_logger -from fixtup.settings import read_settings def fixture_template(identifier: str) -> FixtureTemplate: - settings = read_settings() - fixtures_path = settings.fixtures_dir + fixtup_context = lib_context() + + fixtures_path = fixtup_context.fixturesdir fixture_template = _fixture_template_path(fixtures_path, identifier) config = _read_fixture_yml(fixture_template) return FixtureTemplate.create_from_fixture_template(fixture_template, config) diff --git a/src/fixtup/hook/factory.py b/src/fixtup/hook/factory.py index 39dae2f..13e7166 100644 --- a/src/fixtup/hook/factory.py +++ b/src/fixtup/hook/factory.py @@ -1,17 +1,15 @@ -from fixtup.factory import factory, RuntimeContext +from fixtup.context import lib_context from fixtup.hook.base import HookEngine from fixtup.hook.mock import MockHookEngine from fixtup.hook.python import PythonHookEngine -from fixtup.plugin.base import PluginEngine -@factory -def lookup_hook_engine(context: RuntimeContext) -> HookEngine: +def lookup_hook_engine() -> HookEngine: + fixtup_context = lib_context() hook_engine: HookEngine - if context.enable_hooks: + if fixtup_context.enable_hooks is True: hook_engine = PythonHookEngine() else: hook_engine = MockHookEngine() - return hook_engine diff --git a/src/fixtup/hook/mock.py b/src/fixtup/hook/mock.py index fc3a6f6..d87469a 100644 --- a/src/fixtup/hook/mock.py +++ b/src/fixtup/hook/mock.py @@ -1,10 +1,9 @@ +from fixtup import logger from fixtup.entity.fixture_template import FixtureTemplate from fixtup.hook.base import HookEngine, HookEvent -from fixtup.logger import get_logger class MockHookEngine(HookEngine): def run(self, event: HookEvent, template: FixtureTemplate) -> None: - logger = get_logger() logger.debug(f"run mock for hook {event.value} in {template.identifier}") diff --git a/src/fixtup/hook/python.py b/src/fixtup/hook/python.py index e1d94f6..1228a34 100644 --- a/src/fixtup/hook/python.py +++ b/src/fixtup/hook/python.py @@ -1,20 +1,19 @@ -import io import os import subprocess import sys from typing import Optional +from fixtup import logger from fixtup.entity.fixture_template import FixtureTemplate from fixtup.exceptions import HookRuntimeError from fixtup.hook.base import HookEngine, HookEvent from fixtup.lib.env import env_override -from fixtup.logger import get_logger class PythonHookEngine(HookEngine): def run(self, event: HookEvent, template: FixtureTemplate) -> None: - logger = get_logger() + hook_directory = os.path.join(template.directory, '.hooks') if not os.path.isdir(hook_directory): logger.debug(f"fixture {template.identifier} does not expose hook directory in {hook_directory}") diff --git a/src/fixtup/logger.py b/src/fixtup/logger.py index 9513301..76bb6e4 100644 --- a/src/fixtup/logger.py +++ b/src/fixtup/logger.py @@ -1,6 +1,59 @@ +from typing import Optional + import logging +DEFAULT_LOGGER = 'fixtup' + +def warning(*args, name: Optional[str] = None, **kwargs) -> None: + """ + log a warning message. It use the same signature as `Logger.warning``. + + >>> logger.warning("this is a warning message") + >>> logger.warning("this is a warning message", name="customlogger") + """ + logger = get_logger(name) + logger.warning(*args, **kwargs) + + +def info(*args, name: Optional[str] = None, **kwargs) -> None: + """ + log a info message. It use the same signature as `Logger.info``. + + >>> logger.info("this is an info message") + >>> logger.info("this is an info message", name="customlogger") + """ + logger = get_logger(name) + logger.info(*args, **kwargs) + + +def debug(*args, name: Optional[str] = None, **kwargs) -> None: + """ + log a debug message. It use the same signature as `Logger.debug``. + + >>> logger.debug("this is a debug message") + >>> logger.debug("this is a debug message", name="customlogger") + """ + logger = get_logger(name) + logger.debug(*args, **kwargs) -def get_logger(): - logger = logging.getLogger('fixtup') + +def error(*args, name: Optional[str] = None, **kwargs) -> None: + """ + log a error message. It use the same signature as `Logger.error``. + + >>> logger.error("this is an error message") + >>> logger.error("this is an error message", name="customlogger") + """ + logger = get_logger(name) + logger.error(*args, **kwargs) + + +def get_logger(name: Optional[str] = None) -> logging.Logger: + if name is None: + name = DEFAULT_LOGGER + + logger = logging.getLogger(name) return logger + + + diff --git a/src/fixtup/plugin/base.py b/src/fixtup/plugin/base.py index e7050b5..79e13fb 100644 --- a/src/fixtup/plugin/base.py +++ b/src/fixtup/plugin/base.py @@ -1,5 +1,5 @@ +from fixtup import logger from fixtup.entity.plugin import PluginEvent -from fixtup.logger import get_logger class PluginEngine: @@ -15,7 +15,6 @@ def release(self, event: PluginEvent, *args, **kwargs) -> None: This method try to release resource instanciate through a plugin. We run the event in degraded mode. If it raise an error, it will ignore it. """ - logger = get_logger() logger.warning(f"release plugin resource due to internal error with event {event}") try: self.run(event, *args, **kwargs) diff --git a/src/fixtup/plugin/factory.py b/src/fixtup/plugin/factory.py index 55a33aa..0421463 100644 --- a/src/fixtup/plugin/factory.py +++ b/src/fixtup/plugin/factory.py @@ -1,20 +1,19 @@ -from fixtup.factory import factory, RuntimeContext +from fixtup.context import lib_context from fixtup.plugin.base import PluginEngine from fixtup.plugin.mock import MockPluginEngine from fixtup.plugin.python import PythonPluginEngine -from fixtup.settings import read_settings -@factory -def lookup_plugin_engine(context: RuntimeContext) -> PluginEngine: +def lookup_plugin_engine() -> PluginEngine: + fixtup_context = lib_context() plugin_engine: PluginEngine - if context.enable_plugins: + if fixtup_context.enable_plugins is True: plugin_engine = PythonPluginEngine() else: plugin_engine = MockPluginEngine() - settings = read_settings() - for plugin in settings.plugins: + # settings = read_settings() + for plugin in fixtup_context.plugins: plugin_engine.register_plugin(plugin) return plugin_engine diff --git a/src/fixtup/plugin/mock.py b/src/fixtup/plugin/mock.py index b5370b9..42e7c4c 100644 --- a/src/fixtup/plugin/mock.py +++ b/src/fixtup/plugin/mock.py @@ -4,7 +4,7 @@ import attr -from fixtup.logger import get_logger +from fixtup import logger from fixtup.plugin.base import PluginEngine, PluginEvent, event_to_function @attr.s @@ -12,17 +12,14 @@ class MockPluginEngine(PluginEngine): plugins: List[ModuleType] = attr.ib(factory=list) def run(self, event: PluginEvent, *args, **kwargs): - logger = get_logger() function = event_to_function(event) for plugin in self.plugins: if hasattr(plugin, function): logger.debug(f"run mock for plugin {plugin.__name__} for hook {event.value}") def register_plugin(self, module: str): - logger = get_logger() try: plugin = importlib.import_module(module) self.plugins.append(plugin) except ModuleNotFoundError: - logger.error( - f'plugin "{module}" is missing, check if the module "{module}" is installed in your python venv') + logger.error(f'plugin "{module}" is missing, check if the module "{module}" is installed in your python venv') diff --git a/src/fixtup/plugin/python.py b/src/fixtup/plugin/python.py index 2c4b6e3..d17d7d2 100644 --- a/src/fixtup/plugin/python.py +++ b/src/fixtup/plugin/python.py @@ -4,9 +4,9 @@ import attr +from fixtup import logger from fixtup.entity.plugin import Plugin from fixtup.exceptions import PluginRuntimeError -from fixtup.logger import get_logger from fixtup.plugin.base import PluginEngine, PluginEvent, event_to_function @@ -26,7 +26,6 @@ def run(self, event: PluginEvent, *args, **kwargs): raise PluginRuntimeError(str(exception), plugin) from exception def register_plugin(self, module: str): - logger = get_logger() try: _module = importlib.import_module(module) plugin = Plugin.create_from_module(_module) diff --git a/src/fixtup/prompt/factory.py b/src/fixtup/prompt/factory.py index 68c3a15..d6918cd 100644 --- a/src/fixtup/prompt/factory.py +++ b/src/fixtup/prompt/factory.py @@ -1,15 +1,19 @@ -from fixtup.factory import factory, RuntimeContext +from fixtup.context import lib_context +from fixtup.entity.fixtup import Driver from fixtup.prompt import Prompt -@factory -def lookup_prompt(context: RuntimeContext) -> Prompt: +def lookup_prompt() -> Prompt: """ """ - from fixtup.prompt.prompt_toolkit import PromptToolkit - from fixtup.prompt.mock import Mock + fixtup_context = lib_context() + driver_prompt = fixtup_context.driver_prompt - if context.unittest: + if driver_prompt == Driver.prompt_toolkit: + from fixtup.prompt.prompt_toolkit import PromptToolkit + return PromptToolkit() + elif driver_prompt == Driver.mock: + from fixtup.prompt.mock import Mock return Mock() else: - return PromptToolkit() + raise NotImplementedError(f"{fixtup_context.driver_prompt} is not supported") diff --git a/src/fixtup/prompt/mock.py b/src/fixtup/prompt/mock.py index aafde8a..eac953e 100644 --- a/src/fixtup/prompt/mock.py +++ b/src/fixtup/prompt/mock.py @@ -1,22 +1,22 @@ -import threading +from types import SimpleNamespace from typing import List, Optional from fixtup.prompt.base import Prompt -thread_store = threading.local() -thread_store.input = [] +p_store = SimpleNamespace() +p_store.input = [] def reset_input() -> None: """ reset inputs given by the user in the automated test """ - thread_store.input = [] + p_store.input = [] def send_text(input: str): - thread_store.input.append(input) + p_store.input.append(input) class Mock(Prompt): @@ -26,22 +26,22 @@ class Mock(Prompt): """ def choice(self, question: str, choices: List[str], default: Optional[str] = None) -> str: - mocked_input: List[str] = thread_store.input + mocked_input: List[str] = p_store.input _input = mocked_input.pop(0) assert _input in choices, f"{_input} does not match the available choices {choices}" return _input def fixture_repository(self) -> str: - mocked_input: List[str] = thread_store.input + mocked_input: List[str] = p_store.input return mocked_input.pop(0) def new_fixture(self, fixture_repository: str) -> str: - mocked_input: List[str] = thread_store.input + mocked_input: List[str] = p_store.input return mocked_input.pop(0) def confirm(self, question: str, default: bool = False) -> bool: - mocked_input: List[str] = thread_store.input + mocked_input: List[str] = p_store.input _input = mocked_input.pop(0) if _input == "": return default @@ -49,7 +49,7 @@ def confirm(self, question: str, default: bool = False) -> bool: return _input == "y" def input(self, question: str, default: Optional[str] = None) -> Optional[str]: - mocked_input: List[str] = thread_store.input + mocked_input: List[str] = p_store.input _input = mocked_input.pop(0) if _input == "": return default diff --git a/src/fixtup/scaffold/base.py b/src/fixtup/scaffold/base.py index 93651b7..8260a95 100644 --- a/src/fixtup/scaffold/base.py +++ b/src/fixtup/scaffold/base.py @@ -5,10 +5,10 @@ from jinja2 import Environment import yaml +from fixtup import logger from fixtup.entity.fixture_template import FixtureTemplate from fixtup.entity.plugin import PluginEvent from fixtup.entity.settings import Settings -from fixtup.logger import get_logger from fixtup.plugin.factory import lookup_plugin_engine RESOURCE_DIR = os.path.realpath(os.path.join(__file__, '..', 'resource')) @@ -23,7 +23,6 @@ def scaffold_fixture_repository(settings: Settings): :param settings: :return: """ - logger = get_logger() logger.debug(f'scaffold fixture repository: {settings.fixtures_dir}') os.makedirs(settings.fixtures_dir) @@ -33,7 +32,6 @@ def scaffold_new_fixture(fixture: FixtureTemplate): :param fixture: the fixture definition :return: """ - logger = get_logger() logger.debug(f'scaffold new fixture: {fixture}') os.makedirs(fixture.directory) diff --git a/src/fixtup/settings/base.py b/src/fixtup/settings/base.py index 5321cef..c548cfa 100644 --- a/src/fixtup/settings/base.py +++ b/src/fixtup/settings/base.py @@ -1,6 +1,7 @@ import os from typing import Optional, List +from fixtup.context import lib_context from fixtup.entity.project_manifest import ProjectManifests from fixtup.entity.settings import Settings from fixtup.exceptions import PythonManifestMissing, FixtupSettingsMissing, FixtupSettingsAlreadyPresent @@ -62,6 +63,23 @@ def list_project_manifests() -> ProjectManifests: return project_manifests +def load_settings() -> None: + """ + load the project settings into fixtup context + + >>> load_settings() + """ + fixtup = lib_context() + settings = read_settings() + + fixtup.projectloaded = True + fixtup.projectdir = settings.projectdir + fixtup.plugins = settings.plugins + fixtup.manifestpath = settings.manifestpath + fixtup.fixturesdir = settings.fixtures_dir + return + + def read_settings() -> Settings: """ Read the settings of fixtup into the python project manifest @@ -104,8 +122,8 @@ def write_settings(settings: Settings): parsers = lookup_parsers() for parser in parsers: if settings.manifest_identifier == parser.manifest: - if settings.configuration_dir is not None and parser.contains_settings(settings.configuration_dir): - raise FixtupSettingsAlreadyPresent(f"fail to write settings because it already exists in {settings.manifest_path}") + if settings.projectdir is not None and parser.contains_settings(settings.projectdir): + raise FixtupSettingsAlreadyPresent(f"fail to write settings because it already exists in {settings.manifestpath}") parser.append_settings(settings) return diff --git a/src/fixtup/settings/factory.py b/src/fixtup/settings/factory.py index e8a1b08..5b6e6d6 100644 --- a/src/fixtup/settings/factory.py +++ b/src/fixtup/settings/factory.py @@ -1,14 +1,11 @@ import typing from typing import List -from fixtup.factory import factory, RuntimeContext - if typing.TYPE_CHECKING: from fixtup.settings.base import SettingsParser -@factory -def lookup_parsers(context: RuntimeContext) -> List['SettingsParser']: +def lookup_parsers() -> List['SettingsParser']: """ When looking for a manifest, we first favor the setup.cfg file because the pyproject.toml file is generated by default. It is sometimes not used. diff --git a/src/fixtup/settings/module.py b/src/fixtup/settings/module.py index 4428245..508904e 100644 --- a/src/fixtup/settings/module.py +++ b/src/fixtup/settings/module.py @@ -1,11 +1,8 @@ import threading from fixtup.entity.settings import Settings -from fixtup.logger import get_logger from fixtup.settings.base import SettingsParser -logger = get_logger() - thread_store = threading.local() thread_store.settings = None diff --git a/src/fixtup/settings/pyproject_toml.py b/src/fixtup/settings/pyproject_toml.py index 152fb57..a82bda4 100644 --- a/src/fixtup/settings/pyproject_toml.py +++ b/src/fixtup/settings/pyproject_toml.py @@ -3,13 +3,11 @@ import toml +from fixtup import logger from fixtup.entity.settings import Settings -from fixtup.logger import get_logger from fixtup.os import universal_path from fixtup.settings.base import SettingsParser, RESOURCE_DIR -logger = get_logger() - class PyprojectToml(SettingsParser): manifest = "pyproject.toml" @@ -61,7 +59,7 @@ def append_settings(self, settings: Settings): """ write the settings at the end of the manifres """ - manifest_expected_path = self._manifest_expected_path(settings.configuration_dir) + manifest_expected_path = self._manifest_expected_path(settings.projectdir) self._assert_manifest_exists(manifest_expected_path) with io.open(os.path.join(RESOURCE_DIR, 'pyproject_toml_settings.in')) as file_pointer: diff --git a/src/fixtup/settings/setup_cfg.py b/src/fixtup/settings/setup_cfg.py index 8d1856a..7e6af5a 100644 --- a/src/fixtup/settings/setup_cfg.py +++ b/src/fixtup/settings/setup_cfg.py @@ -3,15 +3,12 @@ import configparser +from fixtup import logger from fixtup.entity.settings import Settings -from fixtup.logger import get_logger from fixtup.os import universal_path from fixtup.settings.base import SettingsParser, RESOURCE_DIR -logger = get_logger() - - class SetupCfg(SettingsParser): manifest = "setup.cfg" @@ -65,7 +62,7 @@ def append_settings(self, settings: Settings): """ write the settings at the end of the manifest """ - manifest_expected_path = self._manifest_expected_path(settings.configuration_dir) + manifest_expected_path = self._manifest_expected_path(settings.projectdir) self._assert_manifest_exists(manifest_expected_path) with io.open(os.path.join(RESOURCE_DIR, 'setup_cfg_settings.in')) as file_pointer: diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/acceptances/test_cli.py b/tests/acceptances/test_cli.py index f0172e3..bbe3861 100644 --- a/tests/acceptances/test_cli.py +++ b/tests/acceptances/test_cli.py @@ -5,7 +5,8 @@ import fixtup from fixtup.cli.base import cli -from fixtup.factory import RuntimeContext, reset_runtime_context +from fixtup.context import lib_context_inject, lib_context_eject +from fixtup.entity.fixtup import Driver from fixtup.prompt.mock import send_text, reset_input from fixtup.settings import read_settings from fixtup.tests.settings import override_fixtup_settings @@ -14,12 +15,13 @@ class TestCli(unittest.TestCase): def setUp(self): + self.context = lib_context_inject() + self.context.driver_prompt = Driver.mock reset_input() - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=False)) self._runner = CliRunner() def tearDown(self) -> None: - reset_runtime_context() + lib_context_eject() def test_invoke_should_show_a_list_of_command(self): # Arrange @@ -121,8 +123,6 @@ def test_new_should_generate_a_new_fixture(self): def test_new_should_ask_for_docker_template(self): # Arrange - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=True)) - send_text("hello world") send_text("y") send_text("y") @@ -151,8 +151,6 @@ def test_new_should_ask_for_docker_template(self): def test_new_should_ask_for_docker_template_and_not_create_the_template_when_the_user_refuse(self): # Arrange - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=True)) - send_text("hello world") send_text("n") @@ -180,8 +178,6 @@ def test_new_should_ask_for_docker_template_and_not_create_the_template_when_the def test_new_should_ask_for_dotenv_template(self): # Arrange - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=True)) - send_text("hello world") send_text("y") send_text("y") @@ -210,8 +206,6 @@ def test_new_should_ask_for_dotenv_template(self): def test_new_should_ask_for_dotenv_and_not_create_the_template_when_the_user_refuse(self): # Arrange - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=True)) - send_text("hello world") send_text("n") diff --git a/tests/acceptances/test_fixtup.py b/tests/acceptances/test_fixtup.py index 557bc6a..36c872f 100644 --- a/tests/acceptances/test_fixtup.py +++ b/tests/acceptances/test_fixtup.py @@ -1,25 +1,25 @@ -import logging import os import shutil import unittest import fixtup +from fixtup.context import lib_context_inject, lib_context_eject from fixtup.exceptions import FixtureNotFound, PluginRuntimeError -from fixtup.factory import reset_runtime_context, RuntimeContext from fixtup.tests.logger import disable_logging from fixtup.tests.settings import override_fixtup_settings +from fixtures import fixture_context class TestFixtup(unittest.TestCase): def setUp(self): - reset_runtime_context(RuntimeContext(unittest=True, - enable_plugins=False, - enable_hooks=False, - emulate_new_process=True)) + self.context = lib_context_inject() + self.context.enable_hooks = False + self.context.enable_plugins = False + self.context.emulate_new_process = True def tearDown(self) -> None: - reset_runtime_context() + lib_context_eject() def test_up_should_mount_a_fixture_into_tmp_file(self): # Arrange @@ -55,7 +55,7 @@ def test_up_restore_the_working_dir_outside_the_context(self): def test_up_should_reuse_fixtures_through_different_tests_when_keep_up_flag_is_on(self): # Acts - reset_runtime_context(RuntimeContext(unittest=True, emulate_new_process=False)) + self.context.emulate_new_process = False # Arrange with fixtup.up("simple_fixture_keep_up"): @@ -98,7 +98,9 @@ def test_configure_should_override_manifest_settings(self): self.assertTrue(os.path.isfile(os.path.join(cwd, 'hello.txt'))) def test_up_should_show_error_message_when_error_happens_in_plugins(self): - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=True, emulate_new_process=True)) + # Arrange + self.context.enable_plugins = True + self.context.emulate_new_process = True SCRIPT_DIR = os.path.realpath(os.path.join(__file__, '..')) # Acts @@ -142,11 +144,7 @@ def test_up_on_fixture_with_setup_data_hook_should_invoke_the_hook_on_every_up(s between 2 tests, the setup_data hook and the teardown_data hook are invoked to mount and clean the data. """ - reset_runtime_context(RuntimeContext(unittest=True, - enable_plugins=False, - enable_hooks=True, - emulate_new_process=True)) - + self.context.enable_hooks = True SCRIPT_DIR = os.path.realpath(os.path.join(__file__, '..')) # Acts @@ -171,7 +169,6 @@ def test_up_on_fixture_with_setup_data_hook_should_invoke_the_hook_on_every_up(s self.assertFalse(os.path.isfile(os.path.join(fixture_cwd, 'file.data'))) def test_up_on_fixture_without_policies_should_create_a_new_fixture_environment(self): - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=False, emulate_new_process=False)) SCRIPT_DIR = os.path.realpath(os.path.join(__file__, '..')) # Acts @@ -190,8 +187,6 @@ def test_up_on_fixture_without_policies_should_create_a_new_fixture_environment( self.assertNotEqual(fixture1, fixture2) def test_up_with_multiple_fixtures_should_create_all_of_them(self): - # reset_runtime_context(RuntimeContext(unittest=True, emulate_new_process=False)) - with fixtup.up('simple_fixture_docker'): fixture1 = os.getcwd() diff --git a/tests/acceptances/test_helper.py b/tests/acceptances/test_helper.py index 746c893..a3e501b 100644 --- a/tests/acceptances/test_helper.py +++ b/tests/acceptances/test_helper.py @@ -3,8 +3,8 @@ import unittest import fixtup.helper -from fixtup.factory import RuntimeContext, reset_runtime_context -from fixtup.tests.settings import override_fixtup_settings +from fixtup.context import lib_context_inject, lib_context_eject +from fixtures import fixture_context class TestHelper(unittest.TestCase): @@ -13,10 +13,11 @@ def setUp(self): if os.getenv('IGNORE_DOCKER_TESTS', '0') == '1': self.skipTest('this test use docker and is ignored on ci running windows because github action does not support docker on windows') - reset_runtime_context(RuntimeContext(emulate_new_process=True)) + self.context = lib_context_inject() + self.context.emulate_new_process = True def tearDown(self) -> None: - reset_runtime_context() + lib_context_eject() def test_wait_port_should_raise_error_when_timeout_is_spend(self): # Assign diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/fixtup/docker_redis/fixtup.yml b/tests/fixtures/fixtup/docker_redis/fixtup.yml index a1a3ac3..52cf755 100644 --- a/tests/fixtures/fixtup/docker_redis/fixtup.yml +++ b/tests/fixtures/fixtup/docker_redis/fixtup.yml @@ -11,4 +11,4 @@ keep_mounted: False # # This attribute allow to start a database only once and stop the container only when unittest has finished to run # the test suite. It may be interested to improve the performance if your start and stop process is too slow -keep_running: False \ No newline at end of file +keep_up: False diff --git a/tests/fixtures/fixture_context.py b/tests/fixtures/fixture_context.py new file mode 100644 index 0000000..0cc19b6 --- /dev/null +++ b/tests/fixtures/fixture_context.py @@ -0,0 +1,24 @@ +from typing import Generator, Optional + +from contextlib import contextmanager + +from fixtup.context import lib_context_setup, lib_context_teardown, lib_context +from fixtup.entity.fixtup import Fixtup + + +@contextmanager +def use_lib_context() -> Generator[Fixtup, None, None]: + """ + replaces the execution context of fixtup with a context that will be configured in the tests + to be able to test the behavior of fixtup. + + >>> ctx: Context + >>> with fixture_context.use_lib_context() as ctx: + >>> ctx.appdir = '/tmp' + >>> # do something in the test + """ + try: + lib_context_setup() + yield lib_context() + finally: + lib_context_teardown() diff --git a/tests/integrations/fixture/test_fixture_engine.py b/tests/integrations/fixture/test_fixture_engine.py index 0b2d325..bf7183a 100644 --- a/tests/integrations/fixture/test_fixture_engine.py +++ b/tests/integrations/fixture/test_fixture_engine.py @@ -3,21 +3,28 @@ import os import unittest -import fixtup +from fixtup.context import lib_context_inject, lib_context_eject, lib_context_setup, lib_context_teardown from fixtup.entity.fixture import State -from fixtup.factory import RuntimeContext, reset_runtime_context from fixtup.fixture.factory import lookup_fixture_engine from fixtup.fixture_template.base import fixture_template + class TestFixtureEngine(unittest.TestCase): def setUp(self) -> None: + self.context = lib_context_inject() + self.context.emulate_new_process = True + self.context.fixturesdir = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'fixtures', 'fixtup')) + lib_context_setup() + logging.disable(logging.WARNING) - reset_runtime_context(RuntimeContext(emulate_new_process=True)) self.tested = lookup_fixture_engine() + def tearDown(self) -> None: + lib_context_eject() + lib_context_teardown() logging.disable(logging.INFO) def test_new_fixture_should_create_an_empty_directory_in_tmp_file(self): diff --git a/tests/integrations/fixture_template/test_fixture_template.py b/tests/integrations/fixture_template/test_fixture_template.py index cefc9cc..3b5942f 100644 --- a/tests/integrations/fixture_template/test_fixture_template.py +++ b/tests/integrations/fixture_template/test_fixture_template.py @@ -1,18 +1,29 @@ +import os + import unittest from fixtup import fixtup +from fixtup.context import lib_context_inject, lib_context_eject from fixtup.fixture_template.base import fixture_template +from fixtures import fixture_context class TestFixtureTemplate(unittest.TestCase): - def setUp(self): - pass + def setUp(self) -> None: + self.context = lib_context_inject() + self.context.emulate_new_process = True + self.context.fixturesdir = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'fixtures', 'fixtup')) + + def tearDown(self): + lib_context_eject() def test_a_fixture_without_manifest_is_not_keep_up(self): - result = fixture_template("simple_fixture") - self.assertFalse(result.keep_up) + with fixture_context.use_lib_context(): + result = fixture_template("simple_fixture") + self.assertFalse(result.keep_up) def test_a_fixture_with_manifest_should_read_keep_up_flag(self): - result = fixture_template("simple_fixture_keep_up") - self.assertTrue(result.keep_up) + with fixture_context.use_lib_context(): + result = fixture_template("simple_fixture_keep_up") + self.assertTrue(result.keep_up) diff --git a/tests/integrations/plugins/test_dotenv.py b/tests/integrations/plugins/test_dotenv.py index ee0f6f3..d813183 100644 --- a/tests/integrations/plugins/test_dotenv.py +++ b/tests/integrations/plugins/test_dotenv.py @@ -2,13 +2,23 @@ import unittest import fixtup +from fixtup.context import lib_context_inject, lib_context_eject from fixtup.entity.fixture import Fixture from fixtup.plugins import dotenv from fixtup.tests.settings import override_fixtup_settings +from fixtures import fixture_context class TestDotenv(unittest.TestCase): + def setUp(self) -> None: + self.context = lib_context_inject() + self.context.enable_plugins = True + self.context.emulate_new_process = True + + def tearDown(self) -> None: + lib_context_eject() + def test_on_starting_should_override_environments_variables(self): SCRIPT_DIR = os.path.realpath(os.path.join(__file__, '..')) with override_fixtup_settings({ diff --git a/tests/integrations/scaffold/test_base.py b/tests/integrations/scaffold/test_base.py index c2f4234..2dd4f99 100644 --- a/tests/integrations/scaffold/test_base.py +++ b/tests/integrations/scaffold/test_base.py @@ -5,18 +5,20 @@ import yaml import fixtup +from fixtup.context import lib_context_inject, lib_context_eject from fixtup.entity.fixture_template import FixtureTemplate -from fixtup.factory import reset_runtime_context, RuntimeContext from fixtup.scaffold.base import scaffold_new_fixture +from fixtures import fixture_context class TestScaffold(unittest.TestCase): def setUp(self): - reset_runtime_context(RuntimeContext(unittest=True, enable_plugins=False)) + self.context = lib_context_inject() + self.context.enable_plugins = False def tearDown(self) -> None: - reset_runtime_context() + lib_context_eject() def test_scaffold_new_fixture_should_generate_a_fixture_directory(self): # Arrange diff --git a/tests/units/entity/test_settings.py b/tests/units/entity/test_settings.py index a8aff03..64632ce 100644 --- a/tests/units/entity/test_settings.py +++ b/tests/units/entity/test_settings.py @@ -15,7 +15,7 @@ def test_configuration_dir_should_return_the_directory_of_the_manifest(self): # Acts & Assert expected_path = os.path.join("hello", "world") - self.assertIn(expected_path, settings.configuration_dir) + self.assertIn(expected_path, settings.projectdir) def test_from_manifest_should_extract_fixture(self): # Arrange diff --git a/tests/units/plugin/test_python_plugin_engine.py b/tests/units/plugin/test_python_plugin_engine.py index 4b22e88..08f37b2 100644 --- a/tests/units/plugin/test_python_plugin_engine.py +++ b/tests/units/plugin/test_python_plugin_engine.py @@ -1,18 +1,15 @@ -import logging import unittest -from fixtup.logger import get_logger from fixtup.plugin.python import PythonPluginEngine class TestPythonPluginEngine(unittest.TestCase): def setUp(self): - logger = get_logger() self._tested = PythonPluginEngine() def tearDown(self) -> None: - logger = get_logger() + pass def test_register_plugin_should_register_the_plugin(self): # Arrange diff --git a/tests/units/test_factory.py b/tests/units/test_factory.py deleted file mode 100644 index 370cd77..0000000 --- a/tests/units/test_factory.py +++ /dev/null @@ -1,59 +0,0 @@ -import unittest - -from fixtup.factory import RuntimeContext, reset_runtime_context, depends, factory - - -class TestFactory(unittest.TestCase): - def setUp(self): - reset_runtime_context() - - def test_depends_use_runtime_context_in_non_unit_test_mode(self): - # Arrange - def lookup_parsers(context: RuntimeContext) -> str: - if context.unittest: - return "parser a" - else: - return "parser b" - - # Acts - parser = depends(lookup_parsers) - - # Assert - self.assertEqual("parser b", parser) - - def test_depends_use_runtime_context_in_unit_test_mode(self): - # Arrange - reset_runtime_context(RuntimeContext(unittest=True)) - - def lookup_parsers(context: RuntimeContext) -> str: - if context.unittest: - return "parser a" - else: - return "parser b" - - # Acts - parser = depends(lookup_parsers) - - # Assert - self.assertEqual("parser a", parser) - - def test_factory_wrap_method_and_inject_runtime_context_in_unit_test_mode(self): - # Arrange - reset_runtime_context(RuntimeContext(unittest=True)) - - @factory - def lookup_parsers(context: RuntimeContext) -> str: - if context.unittest: - return "parser a" - else: - return "parser b" - - # Acts - parser = lookup_parsers() - - # Assert - self.assertEqual("parser a", parser) - - -if __name__ == '__main__': - unittest.main()