From 035be30f660d2c27a8623b9b847ba7252d3d6b60 Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Fri, 3 May 2024 13:56:40 +0800
Subject: [PATCH 01/27] Fixed false pass if regex meta chars used
providing a list of regex meta charscters could cause a false pass for validation
---
src/masonite/validation/Validator.py | 12 +++++++++---
tests/features/validation/test_validation.py | 18 +++++++++++++++---
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/src/masonite/validation/Validator.py b/src/masonite/validation/Validator.py
index 940bb8e3..c3de2284 100644
--- a/src/masonite/validation/Validator.py
+++ b/src/masonite/validation/Validator.py
@@ -759,10 +759,16 @@ def passes(self, attribute, key, dictionary):
all_clear = False
if self.special != 0:
- special_chars = "[^A-Za-z0-9]"
+ # custom specials are just a string of characters
+ # and may contain regex meta chars.
+ # so we search for them differently
if self.special_chars:
- special_chars = f"[{self.special_chars}]"
- if len(re.findall(special_chars, attribute)) < self.special:
+ special_count = sum(attribute.count(c) for c in self.special_chars)
+ else:
+ std_specials = "[^A-Za-z0-9]"
+ special_count = len(re.findall(std_specials, attribute))
+
+ if special_count < self.special:
self.special_check = False
all_clear = False
diff --git a/tests/features/validation/test_validation.py b/tests/features/validation/test_validation.py
index c43d5cf6..7737bab0 100644
--- a/tests/features/validation/test_validation.py
+++ b/tests/features/validation/test_validation.py
@@ -1677,14 +1677,26 @@ def test_strong(self):
# test custom special characters
validate = Validator().validate(
{
- "password": "secret&-",
+ "password": "$e]cret&-",
},
- strong(["password"], length=5, uppercase=0, special=2, special_chars="*&^", numbers=0, lowercase=4),
+ strong(
+ ["password"],
+ length=5,
+ uppercase=0,
+ special=4,
+ special_chars="^$*&()[]",
+ numbers=0,
+ lowercase=4,
+ ),
)
self.assertEqual(
validate.all(),
- {"password": ["The password field must contain at least 2 of these characters: '*&^'"]},
+ {
+ "password": [
+ "The password field must contain at least 4 of these characters: '^$*&()[]'"
+ ]
+ },
)
validate = Validator().validate(
From dc36c9adc19d12e1be76c7217308a33f2b1d41e8 Mon Sep 17 00:00:00 2001
From: riteshjha
Date: Sun, 19 May 2024 08:48:38 +0545
Subject: [PATCH 02/27] cookie settings from config
---
src/masonite/cookies/Cookie.py | 41 ++++++++++++++++++++--------------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/src/masonite/cookies/Cookie.py b/src/masonite/cookies/Cookie.py
index 4ba03b61..43ee3ee7 100644
--- a/src/masonite/cookies/Cookie.py
+++ b/src/masonite/cookies/Cookie.py
@@ -1,23 +1,17 @@
+from masonite.configuration import config
+
class Cookie:
- def __init__(
- self,
- name,
- value,
- expires=None,
- http_only=True,
- path="/",
- timezone=None,
- secure=False,
- samesite="Strict",
- ):
+ def __init__(self, name, value, **options):
+ self.options = options
+
self.name = name
self.value = value
- self.http_only = http_only
- self.secure = secure
- self.expires = expires
- self.timezone = timezone
- self.samesite = samesite
- self.path = path
+ self.http_only = self.__get_option('http_only', True)
+ self.secure = self.__get_option('secure', False)
+ self.expires = self.__get_option('expires', None)
+ self.timezone = self.__get_option('timezone', None)
+ self.samesite = self.__get_option('samesite', 'Strict')
+ self.path = self.__get_option('path', '/')
def render(self):
response = f"{self.name}={self.value};"
@@ -36,3 +30,16 @@ def render(self):
response += f"SameSite={self.samesite};"
return response
+
+ def __get_option(self, key: str, default:any):
+ """
+ Get cookie options from config/session.py
+ if option key found in options then it return that
+ if not found in options then it will fetch from config
+ if not found in config then use the default value
+ """
+ if key in self.options:
+ return self.options[key]
+ else:
+ cookie = config('session.drivers.cookie')
+ return cookie[key] if key in cookie else default
\ No newline at end of file
From 324ed720f19d67daef2159eaeae6cbe865de2d88 Mon Sep 17 00:00:00 2001
From: riteshjha
Date: Sun, 16 Jun 2024 08:52:14 +0545
Subject: [PATCH 03/27] fixed linting issue
---
src/masonite/cookies/Cookie.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/masonite/cookies/Cookie.py b/src/masonite/cookies/Cookie.py
index 43ee3ee7..a2fb8d3a 100644
--- a/src/masonite/cookies/Cookie.py
+++ b/src/masonite/cookies/Cookie.py
@@ -1,5 +1,6 @@
from masonite.configuration import config
+
class Cookie:
def __init__(self, name, value, **options):
self.options = options
@@ -31,7 +32,7 @@ def render(self):
return response
- def __get_option(self, key: str, default:any):
+ def __get_option(self, key: str, default: any):
"""
Get cookie options from config/session.py
if option key found in options then it return that
@@ -42,4 +43,4 @@ def __get_option(self, key: str, default:any):
return self.options[key]
else:
cookie = config('session.drivers.cookie')
- return cookie[key] if key in cookie else default
\ No newline at end of file
+ return cookie[key] if key in cookie else default
From 17e47d0793245aeef5fb8c987edf4da8e9f427ed Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Fri, 21 Jun 2024 22:19:50 -0400
Subject: [PATCH 04/27] added object check
---
src/masonite/drivers/queue/DatabaseDriver.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/masonite/drivers/queue/DatabaseDriver.py b/src/masonite/drivers/queue/DatabaseDriver.py
index dba74141..4b38d3fe 100644
--- a/src/masonite/drivers/queue/DatabaseDriver.py
+++ b/src/masonite/drivers/queue/DatabaseDriver.py
@@ -89,7 +89,8 @@ def consume(self):
getattr(obj, callback)(*args)
except AttributeError:
- obj(*args)
+ if callable(obj):
+ obj(*args)
self.success(
f"[{job['id']}][{pendulum.now(tz=self.options.get('tz', 'UTC')).to_datetime_string()}] Job Successfully Processed"
From 09f681db109feb3aa5218e0f0d8ff1ec70c00f84 Mon Sep 17 00:00:00 2001
From: Eduardo Aguad
Date: Thu, 4 Jul 2024 20:50:46 -0400
Subject: [PATCH 05/27] Revert "[5.X] Add `logging` feature"
---
.github/workflows/pythonapp.yml | 2 +-
.gitignore | 2 -
Makefile | 2 +-
poetry.lock | 910 ------------------
pyproject.toml | 35 -
requirements.txt | 8 +-
setup.py | 2 -
src/masonite/facades/Log.py | 5 -
src/masonite/facades/Log.pyi | 24 -
src/masonite/facades/__init__.py | 1 -
src/masonite/logging/Logger.py | 126 ---
.../logging/LoggerExceptionsListener.py | 6 -
src/masonite/logging/LoggerFactory.py | 51 -
src/masonite/logging/LoggingProvider.py | 34 -
src/masonite/logging/__init__.py | 1 -
src/masonite/logging/drivers/BaseDriver.py | 76 --
.../logging/drivers/DailyFileDriver.py | 33 -
.../logging/drivers/SingleFileDriver.py | 25 -
src/masonite/logging/drivers/SlackDriver.py | 133 ---
src/masonite/logging/drivers/StackDriver.py | 12 -
src/masonite/logging/drivers/SysLogDriver.py | 29 -
.../logging/drivers/TerminalDriver.py | 52 -
src/masonite/logging/drivers/__init__.py | 6 -
.../core/exceptions/test_exception_handler.py | 4 -
tests/features/logging/test_logger.py | 71 --
tests/integrations/config/logging.py | 34 -
tests/integrations/config/providers.py | 2 -
27 files changed, 6 insertions(+), 1680 deletions(-)
delete mode 100644 poetry.lock
delete mode 100644 pyproject.toml
delete mode 100644 src/masonite/facades/Log.py
delete mode 100644 src/masonite/facades/Log.pyi
delete mode 100644 src/masonite/logging/Logger.py
delete mode 100644 src/masonite/logging/LoggerExceptionsListener.py
delete mode 100644 src/masonite/logging/LoggerFactory.py
delete mode 100644 src/masonite/logging/LoggingProvider.py
delete mode 100644 src/masonite/logging/__init__.py
delete mode 100644 src/masonite/logging/drivers/BaseDriver.py
delete mode 100644 src/masonite/logging/drivers/DailyFileDriver.py
delete mode 100644 src/masonite/logging/drivers/SingleFileDriver.py
delete mode 100644 src/masonite/logging/drivers/SlackDriver.py
delete mode 100644 src/masonite/logging/drivers/StackDriver.py
delete mode 100644 src/masonite/logging/drivers/SysLogDriver.py
delete mode 100644 src/masonite/logging/drivers/TerminalDriver.py
delete mode 100644 src/masonite/logging/drivers/__init__.py
delete mode 100644 tests/features/logging/test_logger.py
delete mode 100644 tests/integrations/config/logging.py
diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml
index 023daaae..8e6b36dd 100644
--- a/.github/workflows/pythonapp.yml
+++ b/.github/workflows/pythonapp.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.9", "3.10", "3.11"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v3
diff --git a/.gitignore b/.gitignore
index ba1ff384..6591d4bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,5 +12,3 @@ src/masonite.egg-info/*
.vscode
build/
venv4
-.python-version
-*.whl
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 727f1964..8981da29 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
init:
cp .env-example .env
- pip install --no-cache-dir --upgrade -r requirements.txt
+ pip install -r requirements.txt
pip install '.[test]'
# Create MySQL Database
# Create Postgres Database
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 1dcfa8c3..00000000
--- a/poetry.lock
+++ /dev/null
@@ -1,910 +0,0 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
-
-[[package]]
-name = "bcrypt"
-version = "3.2.2"
-description = "Modern password hashing for your software and your servers"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "bcrypt-3.2.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e"},
- {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26"},
- {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb"},
- {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a"},
- {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521"},
- {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40"},
- {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa"},
- {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"},
- {file = "bcrypt-3.2.2-cp36-abi3-win32.whl", hash = "sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e"},
- {file = "bcrypt-3.2.2-cp36-abi3-win_amd64.whl", hash = "sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129"},
- {file = "bcrypt-3.2.2.tar.gz", hash = "sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb"},
-]
-
-[package.dependencies]
-cffi = ">=1.1"
-
-[package.extras]
-tests = ["pytest (>=3.2.1,!=3.3.0)"]
-typecheck = ["mypy"]
-
-[[package]]
-name = "certifi"
-version = "2024.7.4"
-description = "Python package for providing Mozilla's CA Bundle."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
- {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
-]
-
-[[package]]
-name = "cffi"
-version = "1.16.0"
-description = "Foreign Function Interface for Python calling C code."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
- {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
- {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
- {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
- {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
- {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
- {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
- {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
- {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
- {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
- {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
- {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
- {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
- {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
- {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
- {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
- {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
- {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
- {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
- {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
- {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
- {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
- {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
- {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
- {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
- {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
- {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
-]
-
-[package.dependencies]
-pycparser = "*"
-
-[[package]]
-name = "charset-normalizer"
-version = "3.3.2"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
- {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
-]
-
-[[package]]
-name = "cleo"
-version = "0.8.1"
-description = "Cleo allows you to create beautiful and testable command-line interfaces."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "cleo-0.8.1-py2.py3-none-any.whl", hash = "sha256:141cda6dc94a92343be626bb87a0b6c86ae291dfc732a57bf04310d4b4201753"},
- {file = "cleo-0.8.1.tar.gz", hash = "sha256:3d0e22d30117851b45970b6c14aca4ab0b18b1b53c8af57bed13208147e4069f"},
-]
-
-[package.dependencies]
-clikit = ">=0.6.0,<0.7.0"
-
-[[package]]
-name = "clikit"
-version = "0.6.2"
-description = "CliKit is a group of utilities to build beautiful and testable command line interfaces."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "clikit-0.6.2-py2.py3-none-any.whl", hash = "sha256:71268e074e68082306e23d7369a7b99f824a0ef926e55ba2665e911f7208489e"},
- {file = "clikit-0.6.2.tar.gz", hash = "sha256:442ee5db9a14120635c5990bcdbfe7c03ada5898291f0c802f77be71569ded59"},
-]
-
-[package.dependencies]
-crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""}
-pastel = ">=0.2.0,<0.3.0"
-pylev = ">=1.3,<2.0"
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-description = "Cross-platform colored terminal text."
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-files = [
- {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
- {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
-]
-
-[[package]]
-name = "crashtest"
-version = "0.3.1"
-description = "Manage Python errors with ease"
-optional = false
-python-versions = ">=3.6,<4.0"
-files = [
- {file = "crashtest-0.3.1-py3-none-any.whl", hash = "sha256:300f4b0825f57688b47b6d70c6a31de33512eb2fa1ac614f780939aa0cf91680"},
- {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"},
-]
-
-[[package]]
-name = "cryptography"
-version = "36.0.2"
-description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
- {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"},
- {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"},
- {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"},
- {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"},
- {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"},
- {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"},
- {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"},
- {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"},
- {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
-]
-
-[package.dependencies]
-cffi = ">=1.12"
-
-[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
-docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
-pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
-sdist = ["setuptools-rust (>=0.11.4)"]
-ssh = ["bcrypt (>=3.1.5)"]
-test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
-
-[[package]]
-name = "dotty-dict"
-version = "1.3.1"
-description = "Dictionary wrapper for quick access to deeply nested keys."
-optional = false
-python-versions = ">=3.5,<4.0"
-files = [
- {file = "dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f"},
- {file = "dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15"},
-]
-
-[[package]]
-name = "exceptiongroup"
-version = "1.2.1"
-description = "Backport of PEP 654 (exception groups)"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
- {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
-]
-
-[package.extras]
-test = ["pytest (>=6)"]
-
-[[package]]
-name = "exceptionite"
-version = "2.2.5"
-description = "Exception Handling Made Easy"
-optional = false
-python-versions = "*"
-files = [
- {file = "exceptionite-2.2.5-py3-none-any.whl", hash = "sha256:f6a4ee8a527f972a1cdc4930176fa5b786a362208e96a663a272067abef09d66"},
- {file = "exceptionite-2.2.5.tar.gz", hash = "sha256:3f76eede3eebc3c50e2ed8897609ef7c09bee176fc71ebe3fbe99f6822060dd0"},
-]
-
-[package.dependencies]
-colorama = "*"
-dotty-dict = "*"
-jinja2 = "*"
-mock = "*"
-requests = "*"
-typing-extensions = "*"
-
-[package.extras]
-test = ["black", "coverage", "flake8", "pytest", "pytest-cov", "twine (>=1.5.0)", "wheel"]
-
-[[package]]
-name = "faker"
-version = "13.16.0"
-description = "Faker is a Python package that generates fake data for you."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "Faker-13.16.0-py3-none-any.whl", hash = "sha256:920f94d5aa865fd922bc29f2cf75c75b4d86b30eec23e7174d7513241b759b05"},
- {file = "Faker-13.16.0.tar.gz", hash = "sha256:25c5be99bc5fd8676eea8c1490e0de87f6d9734651c7af2cefc99b322b2936f4"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.4"
-
-[[package]]
-name = "hashids"
-version = "1.3.1"
-description = "Implements the hashids algorithm in python. For more information, visit http://hashids.org/"
-optional = false
-python-versions = ">=2.7"
-files = [
- {file = "hashids-1.3.1-py2.py3-none-any.whl", hash = "sha256:8bddd1acba501bfc9306e7e5a99a1667f4f2cacdc20cbd70bcc5ddfa5147c94c"},
- {file = "hashids-1.3.1.tar.gz", hash = "sha256:6c3dc775e65efc2ce2c157a65acb776d634cb814598f406469abef00ae3f635c"},
-]
-
-[package.extras]
-test = ["pytest (>=2.1.0)"]
-
-[[package]]
-name = "hfilesize"
-version = "0.1.0"
-description = "Human Readable File Sizes"
-optional = false
-python-versions = "*"
-files = [
- {file = "hfilesize-0.1.0.zip", hash = "sha256:560578c395a3b9b0fd25f93c6fb75008e0797cff48b462656c2bc7373179fa30"},
-]
-
-[[package]]
-name = "hupper"
-version = "1.10.3"
-description = "Integrated process monitor for developing and reloading daemons."
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
-files = [
- {file = "hupper-1.10.3-py2.py3-none-any.whl", hash = "sha256:f683850d62598c02faf3c7cdaaa727d8cbe3c5a2497a5737a8358386903b2601"},
- {file = "hupper-1.10.3.tar.gz", hash = "sha256:cd6f51b72c7587bc9bce8a65ecd025a1e95f1b03284519bfe91284d010316cd9"},
-]
-
-[package.extras]
-docs = ["Sphinx", "pylons-sphinx-themes", "watchdog"]
-testing = ["mock", "pytest", "pytest-cov", "watchdog"]
-
-[[package]]
-name = "idna"
-version = "3.7"
-description = "Internationalized Domain Names in Applications (IDNA)"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
- {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
-]
-
-[[package]]
-name = "inflection"
-version = "0.3.1"
-description = "A port of Ruby on Rails inflector to Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "inflection-0.3.1.tar.gz", hash = "sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca"},
-]
-
-[[package]]
-name = "iniconfig"
-version = "2.0.0"
-description = "brain-dead simple config-ini parsing"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
- {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
-]
-
-[[package]]
-name = "jinja2"
-version = "3.0.3"
-description = "A very fast and expressive template engine."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
- {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[[package]]
-name = "markupsafe"
-version = "2.1.5"
-description = "Safely add untrusted strings to HTML/XML markup."
-optional = false
-python-versions = ">=3.7"
-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"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
- {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
-]
-
-[[package]]
-name = "masonite-orm"
-version = "2.22.1"
-description = "The Official Masonite ORM"
-optional = false
-python-versions = "*"
-files = [
- {file = "masonite_orm-2.22.1.tar.gz", hash = "sha256:b49bed04af67bdff4b791970a351f45d1890e19f7852c1974be18b48637bccb2"},
-]
-
-[package.dependencies]
-cleo = ">=0.8.0,<0.9"
-faker = ">=4.1.0,<14.0"
-inflection = ">=0.3,<0.6"
-pendulum = ">=2.1,<2.2"
-
-[package.extras]
-test = ["coverage", "pytest"]
-
-[[package]]
-name = "mock"
-version = "5.1.0"
-description = "Rolling backport of unittest.mock for all Pythons"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744"},
- {file = "mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d"},
-]
-
-[package.extras]
-build = ["blurb", "twine", "wheel"]
-docs = ["sphinx"]
-test = ["pytest", "pytest-cov"]
-
-[[package]]
-name = "packaging"
-version = "24.1"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
- {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
-]
-
-[[package]]
-name = "pastel"
-version = "0.2.1"
-description = "Bring colors to your terminal."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"},
- {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"},
-]
-
-[[package]]
-name = "pendulum"
-version = "2.1.2"
-description = "Python datetimes made easy"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "pendulum-2.1.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe"},
- {file = "pendulum-2.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739"},
- {file = "pendulum-2.1.2-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394"},
- {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0"},
- {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3"},
- {file = "pendulum-2.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b"},
- {file = "pendulum-2.1.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360"},
- {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0"},
- {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087"},
- {file = "pendulum-2.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db"},
- {file = "pendulum-2.1.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002"},
- {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5"},
- {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b"},
- {file = "pendulum-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b"},
- {file = "pendulum-2.1.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116"},
- {file = "pendulum-2.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052"},
- {file = "pendulum-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be"},
- {file = "pendulum-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269"},
- {file = "pendulum-2.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a"},
- {file = "pendulum-2.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7"},
- {file = "pendulum-2.1.2.tar.gz", hash = "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.6,<3.0"
-pytzdata = ">=2020.1"
-
-[[package]]
-name = "pluggy"
-version = "1.5.0"
-description = "plugin and hook calling mechanisms for python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
- {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
-]
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "pycparser"
-version = "2.22"
-description = "C parser in Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
- {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
-]
-
-[[package]]
-name = "pyjwt"
-version = "2.4.0"
-description = "JSON Web Token implementation in Python"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"},
- {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"},
-]
-
-[package.extras]
-crypto = ["cryptography (>=3.3.1)"]
-dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
-docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
-tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
-
-[[package]]
-name = "pylev"
-version = "1.4.0"
-description = "A pure Python Levenshtein implementation that's not freaking GPL'd."
-optional = false
-python-versions = "*"
-files = [
- {file = "pylev-1.4.0-py2.py3-none-any.whl", hash = "sha256:7b2e2aa7b00e05bb3f7650eb506fc89f474f70493271a35c242d9a92188ad3dd"},
- {file = "pylev-1.4.0.tar.gz", hash = "sha256:9e77e941042ad3a4cc305dcdf2b2dec1aec2fbe3dd9015d2698ad02b173006d1"},
-]
-
-[[package]]
-name = "pytest"
-version = "7.4.4"
-description = "pytest: simple powerful testing with Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
- {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
-
-[package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
-
-[[package]]
-name = "python-dateutil"
-version = "2.9.0.post0"
-description = "Extensions to the standard Python datetime module"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-files = [
- {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
- {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
-]
-
-[package.dependencies]
-six = ">=1.5"
-
-[[package]]
-name = "python-dotenv"
-version = "0.15.0"
-description = "Add .env support to your django/flask apps in development and deployments"
-optional = false
-python-versions = "*"
-files = [
- {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
- {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
-]
-
-[package.extras]
-cli = ["click (>=5.0)"]
-
-[[package]]
-name = "pytzdata"
-version = "2020.1"
-description = "The Olson timezone database for Python."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "pytzdata-2020.1-py2.py3-none-any.whl", hash = "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f"},
- {file = "pytzdata-2020.1.tar.gz", hash = "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540"},
-]
-
-[[package]]
-name = "requests"
-version = "2.32.3"
-description = "Python HTTP for Humans."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
- {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
-]
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<4"
-idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<3"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)"]
-use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-
-[[package]]
-name = "requests-file"
-version = "2.1.0"
-description = "File transport adapter for Requests"
-optional = false
-python-versions = "*"
-files = [
- {file = "requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c"},
- {file = "requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658"},
-]
-
-[package.dependencies]
-requests = ">=1.0.0"
-
-[[package]]
-name = "setuptools"
-version = "70.2.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
- {file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
-]
-
-[package.extras]
-doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-
-[[package]]
-name = "six"
-version = "1.16.0"
-description = "Python 2 and 3 compatibility utilities"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
- {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-
-[[package]]
-name = "tldextract"
-version = "2.2.3"
-description = "Accurately separate the TLD from the registered domain and subdomains of a URL, using the Public Suffix List. By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "tldextract-2.2.3-py2.py3-none-any.whl", hash = "sha256:c2a8a392edf3ea6fa8be80930f04c3ac29e91fa604cb2139bdf6a37fc1e1ac6d"},
- {file = "tldextract-2.2.3.tar.gz", hash = "sha256:ab0e38977a129c72729476d5f8c85a8e1f8e49e9202e1db8dca76e95da7be9a8"},
-]
-
-[package.dependencies]
-idna = "*"
-requests = ">=2.1.0"
-requests-file = ">=1.4"
-
-[[package]]
-name = "tomli"
-version = "2.0.1"
-description = "A lil' TOML parser"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.12.2"
-description = "Backported and Experimental Type Hints for Python 3.8+"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
- {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
-]
-
-[[package]]
-name = "urllib3"
-version = "2.2.2"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
- {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
-]
-
-[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
-h2 = ["h2 (>=4,<5)"]
-socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
-
-[[package]]
-name = "watchdog"
-version = "2.3.1"
-description = "Filesystem events monitoring"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "watchdog-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1f1200d4ec53b88bf04ab636f9133cb703eb19768a39351cee649de21a33697"},
- {file = "watchdog-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:564e7739abd4bd348aeafbf71cc006b6c0ccda3160c7053c4a53b67d14091d42"},
- {file = "watchdog-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:95ad708a9454050a46f741ba5e2f3468655ea22da1114e4c40b8cbdaca572565"},
- {file = "watchdog-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a073c91a6ef0dda488087669586768195c3080c66866144880f03445ca23ef16"},
- {file = "watchdog-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa8b028750b43e80eea9946d01925168eeadb488dfdef1d82be4b1e28067f375"},
- {file = "watchdog-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964fd236cd443933268ae49b59706569c8b741073dbfd7ca705492bae9d39aab"},
- {file = "watchdog-2.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:91fd146d723392b3e6eb1ac21f122fcce149a194a2ba0a82c5e4d0ee29cd954c"},
- {file = "watchdog-2.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efe3252137392a471a2174d721e1037a0e6a5da7beb72a021e662b7000a9903f"},
- {file = "watchdog-2.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:85bf2263290591b7c5fa01140601b64c831be88084de41efbcba6ea289874f44"},
- {file = "watchdog-2.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f2df370cd8e4e18499dd0bfdef476431bcc396108b97195d9448d90924e3131"},
- {file = "watchdog-2.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ea5d86d1bcf4a9d24610aa2f6f25492f441960cf04aed2bd9a97db439b643a7b"},
- {file = "watchdog-2.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6f5d0f7eac86807275eba40b577c671b306f6f335ba63a5c5a348da151aba0fc"},
- {file = "watchdog-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b848c71ef2b15d0ef02f69da8cc120d335cec0ed82a3fa7779e27a5a8527225"},
- {file = "watchdog-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0d9878be36d2b9271e3abaa6f4f051b363ff54dbbe7e7df1af3c920e4311ee43"},
- {file = "watchdog-2.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cd61f98cb37143206818cb1786d2438626aa78d682a8f2ecee239055a9771d5"},
- {file = "watchdog-2.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3d2dbcf1acd96e7a9c9aefed201c47c8e311075105d94ce5e899f118155709fd"},
- {file = "watchdog-2.3.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03f342a9432fe08107defbe8e405a2cb922c5d00c4c6c168c68b633c64ce6190"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7a596f9415a378d0339681efc08d2249e48975daae391d58f2e22a3673b977cf"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:0e1dd6d449267cc7d6935d7fe27ee0426af6ee16578eed93bacb1be9ff824d2d"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_i686.whl", hash = "sha256:7a1876f660e32027a1a46f8a0fa5747ad4fcf86cb451860eae61a26e102c8c79"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:2caf77ae137935c1466f8cefd4a3aec7017b6969f425d086e6a528241cba7256"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:53f3e95081280898d9e4fc51c5c69017715929e4eea1ab45801d5e903dd518ad"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:9da7acb9af7e4a272089bd2af0171d23e0d6271385c51d4d9bde91fe918c53ed"},
- {file = "watchdog-2.3.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8a4d484e846dcd75e96b96d80d80445302621be40e293bfdf34a631cab3b33dc"},
- {file = "watchdog-2.3.1-py3-none-win32.whl", hash = "sha256:a74155398434937ac2780fd257c045954de5b11b5c52fc844e2199ce3eecf4cf"},
- {file = "watchdog-2.3.1-py3-none-win_amd64.whl", hash = "sha256:5defe4f0918a2a1a4afbe4dbb967f743ac3a93d546ea4674567806375b024adb"},
- {file = "watchdog-2.3.1-py3-none-win_ia64.whl", hash = "sha256:4109cccf214b7e3462e8403ab1e5b17b302ecce6c103eb2fc3afa534a7f27b96"},
- {file = "watchdog-2.3.1.tar.gz", hash = "sha256:d9f9ed26ed22a9d331820a8432c3680707ea8b54121ddcc9dc7d9f2ceeb36906"},
-]
-
-[package.extras]
-watchmedo = ["PyYAML (>=3.10)"]
-
-[[package]]
-name = "werkzeug"
-version = "2.3.8"
-description = "The comprehensive WSGI web application library."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "werkzeug-2.3.8-py3-none-any.whl", hash = "sha256:bba1f19f8ec89d4d607a3bd62f1904bd2e609472d93cd85e9d4e178f472c3748"},
- {file = "werkzeug-2.3.8.tar.gz", hash = "sha256:554b257c74bbeb7a0d254160a4f8ffe185243f52a52035060b761ca62d977f03"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.1.1"
-
-[package.extras]
-watchdog = ["watchdog (>=2.3)"]
-
-[[package]]
-name = "whitenoise"
-version = "5.2.0"
-description = "Radically simplified static file serving for WSGI applications"
-optional = false
-python-versions = ">=3.5, <4"
-files = [
- {file = "whitenoise-5.2.0-py2.py3-none-any.whl", hash = "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d"},
- {file = "whitenoise-5.2.0.tar.gz", hash = "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7"},
-]
-
-[package.extras]
-brotli = ["Brotli"]
-
-[metadata]
-lock-version = "2.0"
-python-versions = "^3.9"
-content-hash = "e48879056ae0b1a538a554f75c399641dc5a4de7b82d8510244fa0219a709ca2"
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 2e97ae1d..00000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-[tool.poetry]
-authors = ['Joe Mancuso', 'Eduardo Aguad']
-description = "Masonite is a modern and developer centric Python web framework."
-license = "MIT"
-name = "masonite"
-version = "5.0.0"
-
-[tool.poetry.dependencies]
-python = "^3.9"
-inflection = ">=0.3,<0.4"
-exceptionite = ">=2.2,<3.0"
-pendulum = ">=2,<3"
-jinja2 = "<3.1.0"
-cleo = ">=0.8.1,<0.9"
-hupper = ">=1.10,<1.11"
-bcrypt = ">=3.2,<3.3"
-whitenoise = ">=5.2,<5.3"
-python-dotenv = ">=0.15,<0.16"
-masonite-orm = ">=2.14,<3"
-hashids = ">=1.3,<1.4"
-cryptography = ">=36,<37"
-tldextract = ">=2.2,<2.3"
-hfilesize = ">=0.1"
-dotty_dict = ">=1.3.0,<1.40"
-pyjwt = ">=2.3,<2.5"
-pytest = ">=7,<8"
-werkzeug = ">=2,<3"
-watchdog = ">=2,<3"
-
-[tool.poetry.dev-dependencies]
-pytest = "^7.0.0"
-
-[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
diff --git a/requirements.txt b/requirements.txt
index 042198d3..73c7089c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,8 +2,8 @@ argon2-cffi
bcrypt>=3.2,<3.3
black
cleo>=0.8.1,<0.9
-cryptography>=36,<37
-dotty_dict>=1.3.0,<1.40
+cryptography>=3.3.1,<4.0
+dotty_dict>=1.3.0<1.40
exceptionite>=2.0,<3
hashids>=1.3,<1.4
hfilesize>=0.1
@@ -20,7 +20,7 @@ python-dotenv>=0.15,<0.16
responses
slackblocks
tldextract>=2.2,<2.3
-werkzeug>=2,<3
-watchdog>=2,<3
+werkzeug>=2<3
+watchdog>=2<3
whitenoise>=5.2,<5.3
pyjwt>=2.3,<2.5
diff --git a/setup.py b/setup.py
index bdf55212..4ec9aa4f 100644
--- a/setup.py
+++ b/setup.py
@@ -143,8 +143,6 @@
"masonite.helpers",
"masonite.input",
"masonite.loader",
- "masonite.logging",
- "masonite.logging.drivers",
"masonite.mail.drivers",
"masonite.mail",
"masonite.middleware.route",
diff --git a/src/masonite/facades/Log.py b/src/masonite/facades/Log.py
deleted file mode 100644
index 0c5ab7ef..00000000
--- a/src/masonite/facades/Log.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .Facade import Facade
-
-
-class Log(metaclass=Facade):
- key = "logger"
diff --git a/src/masonite/facades/Log.pyi b/src/masonite/facades/Log.pyi
deleted file mode 100644
index 1f6180bf..00000000
--- a/src/masonite/facades/Log.pyi
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import TYPE_CHECKING, List
-
-if TYPE_CHECKING:
- from ..logging.LoggerFactory import LoggerFactory
-
-class Log:
- """Log facade."""
-
- def log(level: str, message: str) -> None:
- """Log a message with the given level."""
- ...
- def debug(message: str) -> None: ...
- def info(message: str) -> None: ...
- def notice(message: str) -> None: ...
- def warning(message: str) -> None: ...
- def error(message: str) -> None: ...
- def critical(message: str) -> None: ...
- def alert(message: str) -> None: ...
- def emergency(message: str) -> None: ...
- def stack(*channels: List[str]) -> "LoggerFactory":
- """On-demand stack channels."""
- ...
- def channel(channel: str) -> "LoggerFactory": ...
- def build(driver: str, options: dict = {}) -> "LoggerFactory": ...
diff --git a/src/masonite/facades/__init__.py b/src/masonite/facades/__init__.py
index 21356582..35f4608d 100644
--- a/src/masonite/facades/__init__.py
+++ b/src/masonite/facades/__init__.py
@@ -17,4 +17,3 @@
from .Cache import Cache
from .RateLimiter import RateLimiter
from .Broadcast import Broadcast
-from .Log import Log
diff --git a/src/masonite/logging/Logger.py b/src/masonite/logging/Logger.py
deleted file mode 100644
index 118756c7..00000000
--- a/src/masonite/logging/Logger.py
+++ /dev/null
@@ -1,126 +0,0 @@
-import logging
-from typing import TYPE_CHECKING, List
-
-if TYPE_CHECKING:
- from ..foundation import Application
-
-from .LoggerFactory import LoggerFactory
-from ..exceptions import InvalidConfigurationSetup
-
-
-class Logger:
- # 0 Emergency: system is unusable
- # 1 Alert: action must be taken immediately
- # 2 Critical: critical conditions
- # 3 Error: error conditions
- # 4 Warning: warning conditions
- # 5 Notice: normal but significant condition
- # 6 Informational: informational messages
- # 7 Debug: debug-level messages
-
- def __init__(self, application: "Application", options: dict = {}) -> None:
- self.application = application
- self.drivers = {}
- self.options = options
-
- # configure python logging module to add new levels
- logging.NOTICE = 25
- logging.ALERT = 60
- logging.EMERGENCY = 70
- new_levels = {
- "notice": logging.NOTICE,
- "alert": logging.ALERT,
- "emergency": logging.EMERGENCY,
- }
- for name, levelno in new_levels.items():
- logging.addLevelName(levelno, name.upper())
-
- self.levels = {
- "debug": logging.DEBUG,
- "info": logging.INFO,
- "notice": logging.NOTICE,
- "warning": logging.WARNING,
- "error": logging.ERROR,
- "critical": logging.CRITICAL,
- "alert": logging.ALERT,
- "emergency": logging.EMERGENCY,
- }
-
- def get_default_level(self) -> str:
- return self.options.get("channels.default.level")
-
- def get_default_timezone(self) -> str:
- return self.options.get("channels.default.timezone")
-
- def get_default_format(self) -> str:
- return self.options.get("channels.default.format")
-
- def get_default_date_format(self) -> str:
- return self.options.get("channels.default.date_format")
-
- def add_driver(self, name: str, driver):
- self.drivers.update({name: driver})
-
- def set_options(self, options: dict) -> "Logger":
- self.options = options
- return self
-
- def get_driver_from_channel(self, channel: str = None, options: dict = {}):
- if channel is None:
- channel = self.options.get("channels.default.driver")
-
- # get driver for channel
- driver_name = self.options.get(f"channels.{channel}.driver")
- if not driver_name:
- raise InvalidConfigurationSetup(
- f"No config for channel '{channel}' in config/logging.py !"
- )
- return self.get_driver(
- driver_name, channel, options or self.options.get(f"channels.{channel}")
- )
-
- def get_driver(self, driver: str, name: str = None, options: dict = {}):
- return self.drivers[driver](self.application, name or driver, options)
-
- def get_level_name(self, levelno: int) -> str:
- for name, no in self.levels.items():
- if no == levelno:
- return name
-
- def log(self, level: str, message: str) -> None:
- """Log a message with the given level."""
- return LoggerFactory(self).log(level, message)
-
- def debug(self, message: str) -> None:
- return LoggerFactory(self).debug(message)
-
- def info(self, message: str) -> None:
- return LoggerFactory(self).info(message)
-
- def notice(self, message: str) -> None:
- return LoggerFactory(self).notice(message)
-
- def warning(self, message: str) -> None:
- return LoggerFactory(self).warning(message)
-
- def error(self, message: str) -> None:
- return LoggerFactory(self).error(message)
-
- def critical(self, message: str) -> None:
- return LoggerFactory(self).critical(message)
-
- def alert(self, message: str) -> None:
- return LoggerFactory(self).alert(message)
-
- def emergency(self, message: str) -> None:
- return LoggerFactory(self).emergency(message)
-
- def stack(self, *channels: List[str]) -> "LoggerFactory":
- """On-demand stack channels."""
- return LoggerFactory(self, driver="stack", options={"channels": channels})
-
- def channel(self, channel: str) -> "LoggerFactory":
- return LoggerFactory(self, channel=channel)
-
- def build(self, driver: str, options: dict = {}) -> "LoggerFactory":
- return LoggerFactory(self, driver, options=options)
diff --git a/src/masonite/logging/LoggerExceptionsListener.py b/src/masonite/logging/LoggerExceptionsListener.py
deleted file mode 100644
index 52670436..00000000
--- a/src/masonite/logging/LoggerExceptionsListener.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from ..facades import Log
-
-
-class LoggerExceptionsListener:
- def handle(self, exception_type: str, exception: Exception):
- Log.error(f"{exception_type}: {exception}")
diff --git a/src/masonite/logging/LoggerFactory.py b/src/masonite/logging/LoggerFactory.py
deleted file mode 100644
index 7320c6bc..00000000
--- a/src/masonite/logging/LoggerFactory.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- from .Logger import Logger
-
-
-class LoggerFactory:
- def __init__(
- self,
- logger: "Logger",
- driver: str = None,
- channel: str = None,
- options: dict = {},
- ) -> None:
- self.logger = logger
- self.driver = driver
- self.channel = channel
- self.options = options
-
- if driver and not channel:
- self.selected_driver = self.logger.get_driver(driver, options=options)
- # log to default configured channel or given channel
- else:
- self.selected_driver = self.logger.get_driver_from_channel(channel, options)
-
- def log(self, level: str, message: str) -> None:
- self.selected_driver.log(level, message)
-
- def debug(self, message: str) -> None:
- self.selected_driver.debug(message)
-
- def info(self, message: str) -> None:
- self.selected_driver.info(message)
-
- def notice(self, message: str) -> None:
- self.selected_driver.notice(message)
-
- def warning(self, message: str) -> None:
- self.selected_driver.warning(message)
-
- def error(self, message: str) -> None:
- self.selected_driver.error(message)
-
- def critical(self, message: str) -> None:
- self.selected_driver.critical(message)
-
- def alert(self, message: str) -> None:
- self.selected_driver.alert(message)
-
- def emergency(self, message: str) -> None:
- self.selected_driver.emergency(message)
diff --git a/src/masonite/logging/LoggingProvider.py b/src/masonite/logging/LoggingProvider.py
deleted file mode 100644
index 2963678c..00000000
--- a/src/masonite/logging/LoggingProvider.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from ..providers import Provider
-from ..facades import Config
-from .Logger import Logger
-from .drivers import (
- TerminalDriver,
- DailyFileDriver,
- SingleFileDriver,
- StackDriver,
- SysLogDriver,
- SlackDriver,
-)
-from .LoggerExceptionsListener import LoggerExceptionsListener
-
-
-class LoggingProvider(Provider):
- def __init__(self, application):
- self.application = application
-
- def register(self):
- logger = Logger(self.application).set_options(Config.get("logging"))
- logger.add_driver("terminal", TerminalDriver)
- logger.add_driver("daily", DailyFileDriver)
- logger.add_driver("single", SingleFileDriver)
- logger.add_driver("stack", StackDriver)
- logger.add_driver("syslog", SysLogDriver)
- logger.add_driver("slack", SlackDriver)
- self.application.bind("logger", logger)
-
- self.application.make("event").listen(
- "masonite.exception.*", [LoggerExceptionsListener]
- )
-
- def boot(self):
- pass
diff --git a/src/masonite/logging/__init__.py b/src/masonite/logging/__init__.py
deleted file mode 100644
index 73e3248e..00000000
--- a/src/masonite/logging/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .LoggingProvider import LoggingProvider
diff --git a/src/masonite/logging/drivers/BaseDriver.py b/src/masonite/logging/drivers/BaseDriver.py
deleted file mode 100644
index 6e82ae24..00000000
--- a/src/masonite/logging/drivers/BaseDriver.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import pendulum
-
-
-class BaseDriver:
- def __init__(self, application, name, options={}):
- self.application = application
- self.options = options
- self.name = name
- self.logger = self.application.make("logger")
- self.levels = self.logger.levels
-
- # for drivers which are using Python logging module
- self.logging_logger = None
- self.logging_handler = None
-
- def set_options(self, options):
- self.options = options
- return self
-
- def get_min_level(self):
- return self.levels.get(
- self.options.get("level", self.logger.get_default_level())
- )
-
- def get_format(self):
- return self.options.get("format", self.logger.get_default_format())
-
- def should_send(self, level):
- return self.get_min_level() <= self.levels.get(level)
-
- def get_time(self):
- return pendulum.now().in_tz(
- self.options.get("timezone", self.logger.get_default_timezone())
- )
-
- def get_formatted_time(self):
- date_format = self.options.get(
- "date_format", self.logger.get_default_date_format()
- )
- return self.get_time().format(date_format)
-
- def set_logger(self):
- for handler in self.logging_logger.handlers:
- self.logging_logger.removeHandler(handler)
- self.logging_logger.addHandler(self.logging_handler)
- self.logging_logger.setLevel(self.get_min_level())
-
- def send(self, level, message):
- raise NotImplementedError()
-
- def log(self, level, message):
- return self.send(self.levels.get(level), message)
-
- def debug(self, message):
- return self.send(self.levels.get("debug"), message)
-
- def info(self, message):
- return self.send(self.levels.get("info"), message)
-
- def notice(self, message):
- return self.send(self.levels.get("notice"), message)
-
- def warning(self, message):
- return self.send(self.levels.get("warning"), message)
-
- def error(self, message):
- return self.send(self.levels.get("error"), message)
-
- def critical(self, message):
- return self.send(self.levels.get("critical"), message)
-
- def alert(self, message):
- return self.send(self.levels.get("alert"), message)
-
- def emergency(self, message):
- return self.send(self.levels.get("emergency"), message)
diff --git a/src/masonite/logging/drivers/DailyFileDriver.py b/src/masonite/logging/drivers/DailyFileDriver.py
deleted file mode 100644
index 315673f1..00000000
--- a/src/masonite/logging/drivers/DailyFileDriver.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import os
-import logging
-from datetime import datetime
-from logging.handlers import TimedRotatingFileHandler
-from .BaseDriver import BaseDriver
-from ...utils.location import base_path
-
-
-class DailyFileDriver(BaseDriver):
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
-
- file_path = datetime.now().strftime("%Y-%m-%d") + ".log"
-
- abs_path = base_path(self.options.get("path", "logs/" + file_path))
- if not os.path.exists(os.path.dirname(abs_path)):
- os.makedirs(os.path.dirname(abs_path))
- self.logging_handler = TimedRotatingFileHandler(
- abs_path,
- when=self.options.get("when", "d"),
- interval=self.options.get("days", 7),
- backupCount=self.options.get("keep", 10),
- )
- self.logging_handler.setFormatter(
- logging.Formatter(self.get_format(), style="{")
- )
- self.logging_logger = logging.getLogger(self.name)
-
- def send(self, level, message):
- self.set_logger()
- self.logging_logger.log(
- level, message, extra={"timestamp": self.get_formatted_time()}
- )
diff --git a/src/masonite/logging/drivers/SingleFileDriver.py b/src/masonite/logging/drivers/SingleFileDriver.py
deleted file mode 100644
index bf5350e8..00000000
--- a/src/masonite/logging/drivers/SingleFileDriver.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import logging
-import os
-from .BaseDriver import BaseDriver
-from ...utils.location import base_path
-
-
-class SingleFileDriver(BaseDriver):
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
- abs_path = base_path(self.options.get("path", "logs/single.log"))
- if not os.path.exists(os.path.dirname(abs_path)):
- os.makedirs(os.path.dirname(abs_path))
- self.logging_handler = logging.FileHandler(
- abs_path,
- )
- self.logging_handler.setFormatter(
- logging.Formatter(self.get_format(), style="{")
- )
- self.logging_logger = logging.getLogger(self.name)
-
- def send(self, level, message):
- self.set_logger()
- self.logging_logger.log(
- level, message, extra={"timestamp": self.get_formatted_time()}
- )
diff --git a/src/masonite/logging/drivers/SlackDriver.py b/src/masonite/logging/drivers/SlackDriver.py
deleted file mode 100644
index 44d4f32f..00000000
--- a/src/masonite/logging/drivers/SlackDriver.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import logging
-import requests
-from typing import Dict, Any, Optional, Tuple
-
-
-from .BaseDriver import BaseDriver
-
-
-class SlackHandler(logging.Handler):
- def __init__(self, url: str) -> None:
- self.url = url
- super(SlackHandler, self).__init__()
- return
-
- def emit(self, record: logging.LogRecord) -> None:
- """emits message"""
- msg: str = self.format(record)
- requests.post(
- self.url, data=str.encode(msg), headers={"Content-type": "application/json"}
- )
- return
-
-
-class SlackFormatter(logging.Formatter):
- def __init__(self, name: str) -> None:
- self.name = name
- super(SlackFormatter, self).__init__()
- return
-
- def format(self, record: logging.LogRecord) -> str:
- import json
-
- def get_traceback() -> str:
- """returns traceback"""
- from traceback import format_exception
-
- exc_info: Tuple = record.exc_info
- return "".join(format_exception(*exc_info)) if exc_info else "None"
-
- def get_color(level: int) -> Optional[str]:
- """returns appropriate color based on logging level"""
- colors: Dict[int, Optional[str]] = {
- 0: None,
- 10: "#FF00FF",
- 20: "good",
- 30: "warning",
- 40: "danger",
- 50: "#660000",
- }
- return colors[level]
-
- data: Dict[str, Any] = {
- "attachments": [
- {
- "color": get_color(record.levelno),
- "title": self.name,
- "text": record.getMessage(),
- "fields": [
- {"title": "Module", "value": record.module, "short": True},
- {
- "title": "Level",
- "value": record.levelname.title(),
- "short": True,
- },
- # {"title": "Function", "value": record.funcName, "short": True},
- # {"title": "Line Number", "value": record.lineno, "short": True},
- # {
- # "title": "Traceback",
- # "value": get_traceback(),
- # "short": False,
- # },
- ],
- "ts": int(record.created),
- }
- ]
- }
- return json.dumps(data)
-
-
-class SlackDriver(BaseDriver):
- """Log message to Slack with Slack API."""
-
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
- self.logging_handler = SlackHandler(self.options.get("webhook_url"))
- self.logging_handler.setFormatter(SlackFormatter(self.name))
- self.logging_logger = logging.getLogger(self.name)
-
- def send(self, level, message):
- self.set_logger()
- self.logging_logger.log(
- level, message, extra={"timestamp": self.get_formatted_time()}
- )
-
- # def send(self, level, message):
- # # here we don't rely on logging module so we have to build
- # text = self.get_format()
- # # level, message, extra={"timestamp": self.get_formatted_time()}
- # payload = {
- # "token": self.token,
- # "channel": self.find_channel(self.channel),
- # "text": message,
- # "username": self.username,
- # "icon_emoji": self.emoji,
- # "as_user": False,
- # "reply_broadcast": True,
- # "unfurl_links": True,
- # "unfurl_media": True,
- # }
- # response = requests.post(self.send_url, payload).json()
- # if not response["ok"]:
- # raise Exception("{}. Check Slack API docs.".format(response["error"]))
-
- # def find_channel(self, name):
- # """Calls the Slack API to find the channel name.
- # This is so we do not have to specify the channel ID's. Slack requires channel ID's
- # to be used.
- # Arguments:
- # name {string} -- The channel name to find.
- # Raises:
- # SlackChannelNotFound -- Thrown if the channel name is not found.
- # Returns:
- # self
- # """
- # response = requests.post(
- # "https://slack.com/api/channels.list", {"token": self.token}
- # )
-
- # for channel in response.json()["channels"]:
- # if channel["name"] == name.split("#")[1]:
- # return channel["id"]
-
- # raise SlackChannelNotFound("Could not find the {} channel".format(name))
diff --git a/src/masonite/logging/drivers/StackDriver.py b/src/masonite/logging/drivers/StackDriver.py
deleted file mode 100644
index 1c2445b2..00000000
--- a/src/masonite/logging/drivers/StackDriver.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from .BaseDriver import BaseDriver
-
-
-class StackDriver(BaseDriver):
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
-
- def send(self, level, message):
- logger = self.application.make("logger")
- level_name = logger.get_level_name(level)
- for channel in self.options.get("channels", []):
- logger.channel(channel).log(level_name, message)
diff --git a/src/masonite/logging/drivers/SysLogDriver.py b/src/masonite/logging/drivers/SysLogDriver.py
deleted file mode 100644
index 684baa62..00000000
--- a/src/masonite/logging/drivers/SysLogDriver.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import logging
-from logging.handlers import SysLogHandler, SYSLOG_UDP_PORT
-
-from .BaseDriver import BaseDriver
-
-
-class SysLogDriver(BaseDriver):
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
- if self.options.get("host") and self.options.get("port"):
- address = (
- self.options.get("host"),
- self.options.get("port"),
- )
- elif self.options.get("address"):
- address = self.options.get("address")
- else:
- address = ("localhost", SYSLOG_UDP_PORT)
- self.logging_handler = SysLogHandler(address)
- self.logging_handler.setFormatter(
- logging.Formatter(self.get_format(), style="{")
- )
- self.logging_logger = logging.getLogger(self.name)
-
- def send(self, level, message):
- self.set_logger()
- self.logging_logger.log(
- level, message, extra={"timestamp": self.get_formatted_time()}
- )
diff --git a/src/masonite/logging/drivers/TerminalDriver.py b/src/masonite/logging/drivers/TerminalDriver.py
deleted file mode 100644
index b5ca2a8d..00000000
--- a/src/masonite/logging/drivers/TerminalDriver.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import logging
-import sys
-
-from .BaseDriver import BaseDriver
-
-
-class ColorFormatter(logging.Formatter):
- """Logging colored formatter, adapted from https://stackoverflow.com/a/56944256/3638629"""
-
- magenta = "\x1b[35m"
- grey = "\x1b[38;21m"
- blue = "\x1b[38;5;39m"
- yellow = "\x1b[38;5;226m"
- red = "\x1b[38;5;196m"
- bold_red = "\x1b[31;1m"
- red_fg_yellow_bg = "\x1b[31;103m"
- white_fg_red_bg = "\x1b[37;41m"
- reset = "\x1b[0m"
-
- def __init__(self, fmt=None, datefmt=None, style="%"):
- super().__init__(fmt, datefmt, style)
- self.fmt = fmt
- self.style = style
- self.FORMATS = {
- logging.DEBUG: self.magenta + self.fmt + self.reset,
- logging.INFO: self.grey + self.fmt + self.reset,
- logging.NOTICE: self.blue + self.fmt + self.reset,
- logging.WARNING: self.yellow + self.fmt + self.reset,
- logging.ERROR: self.red + self.fmt + self.reset,
- logging.CRITICAL: self.bold_red + self.fmt + self.reset,
- logging.ALERT: self.red_fg_yellow_bg + self.fmt + self.reset,
- logging.EMERGENCY: self.white_fg_red_bg + self.fmt + self.reset,
- }
-
- def format(self, record):
- log_fmt = self.FORMATS.get(record.levelno)
- formatter = logging.Formatter(log_fmt, datefmt=self.datefmt, style=self.style)
- return formatter.format(record)
-
-
-class TerminalDriver(BaseDriver):
- def __init__(self, application, name, options):
- super().__init__(application, name, options)
- self.logging_handler = logging.StreamHandler(sys.stdout)
- self.logging_handler.setFormatter(ColorFormatter(self.get_format(), style="{"))
- self.logging_logger = logging.getLogger(name)
-
- def send(self, level, message):
- self.set_logger()
- self.logging_logger.log(
- level, message, extra={"timestamp": self.get_formatted_time()}
- )
diff --git a/src/masonite/logging/drivers/__init__.py b/src/masonite/logging/drivers/__init__.py
deleted file mode 100644
index 15040fdf..00000000
--- a/src/masonite/logging/drivers/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from .TerminalDriver import TerminalDriver
-from .DailyFileDriver import DailyFileDriver
-from .SingleFileDriver import SingleFileDriver
-from .StackDriver import StackDriver
-from .SysLogDriver import SysLogDriver
-from .SlackDriver import SlackDriver
diff --git a/tests/core/exceptions/test_exception_handler.py b/tests/core/exceptions/test_exception_handler.py
index 23cd12d3..6dac2218 100644
--- a/tests/core/exceptions/test_exception_handler.py
+++ b/tests/core/exceptions/test_exception_handler.py
@@ -2,7 +2,6 @@
from src.masonite.routes import Route
from src.masonite.controllers import Controller
from src.masonite.exceptions import RouteNotFoundException
-from src.masonite.facades import Config
class TestController(Controller):
@@ -79,12 +78,9 @@ def test_raising_http_exception_renders_404_error_page(self):
self.get("/http").assertNotFound().assertContains("Page Not Found")
def test_raising_exception_does_not_output_stack_trace_to_console(self):
- # disable logging
- Config.set("logging.channels.default.level", "critical")
with self.debugMode(False):
self.get("/simple")
self.assertConsoleEmpty()
- Config.set("logging.channels.default.level", "info")
def test_accepting_json_returns_500_error_payload(self):
with self.debugMode(False):
diff --git a/tests/features/logging/test_logger.py b/tests/features/logging/test_logger.py
deleted file mode 100644
index 9bcb2948..00000000
--- a/tests/features/logging/test_logger.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import pendulum
-import tempfile
-
-from tests import TestCase
-from src.masonite.facades import Log, Config
-
-
-class TestLogger(TestCase):
- def setUp(self):
- super().setUp()
- self.timestamp = pendulum.datetime(2022, 10, 10, 9, 0, 0)
- self.fakeTime(self.timestamp)
-
- self.log_file_path = Config.get("logging.channels.single.path")
- self.log_file = tempfile.NamedTemporaryFile(mode="w+")
- Config.set("logging.channels.single.path", self.log_file.name)
-
- def tearDown(self):
- super().tearDown()
- self.restoreTime()
- Config.set("logging.channels.single.path", self.log_file)
-
- def test_terminal_logging(self):
- Log.error("message")
- self.assertConsoleOutputContains("2022-10-10 09:00:00 - ERROR: message")
-
- def test_terminal_logging_with_other_level(self):
- Log.info("other message")
- self.assertConsoleOutputContains("2022-10-10 09:00:00 - INFO: other message")
-
- def test_that_messages_under_min_level_are_not_logged(self):
- Log.debug("other message")
- self.assertConsoleEmpty()
-
- def test_timezone_can_be_changed(self):
- old_timezone = Config.get("logging.channels.default.timezone")
- Config.set("logging.channels.default.timezone", "Europe/Paris")
-
- Log.info("other message")
- timestamp_in_tz = self.timestamp.in_tz("Europe/Paris").format(
- "YYYY-MM-DD HH:mm:ss"
- )
- self.assertConsoleOutputContains(f"{timestamp_in_tz} - INFO: other message")
-
- Config.set("logging.channels.default.timezone", old_timezone)
-
- def test_stack_channels(self):
- Log.stack("single", "console").warning("message")
- # check logged in file
- self.assertEqual(
- str(self.log_file.readline()),
- "2022-10-10 09:00:00 - WARNING: message\n",
- )
- # check logged in console
- self.assertConsoleOutputContains("2022-10-10 09:00:00 - WARNING: message")
-
- def test_file_logging(self):
- Log.channel("single").warning("Some warning")
- self.assertEqual(
- str(self.log_file.readline()),
- "2022-10-10 09:00:00 - WARNING: Some warning\n",
- )
-
- def test_on_demand_channel(self):
- Log.build(
- "daily", {"path": self.log_file.name, "format": "{message}"}
- ).critical("Some message")
- self.assertEqual(
- self.log_file.readline(),
- "Some message\n",
- )
diff --git a/tests/integrations/config/logging.py b/tests/integrations/config/logging.py
deleted file mode 100644
index 7868e14e..00000000
--- a/tests/integrations/config/logging.py
+++ /dev/null
@@ -1,34 +0,0 @@
-CHANNELS = {
- "default": {
- "driver": "console",
- "level": "info",
- "timezone": "UTC",
- "format": "{timestamp} - {levelname}: {message}",
- "date_format": "YYYY-MM-DD HH:mm:ss",
- },
- "console": {
- "driver": "terminal",
- # "format": "> {timestamp} - {levelname}: {message}",
- # "date_format": "YYYY-MM-DD HH:mm:ss",
- },
- "single": {
- "driver": "single",
- "path": "logs/masonite.log",
- },
- "daily": {
- "driver": "daily",
- "days": 7,
- "keep": 10,
- },
- "all": {"driver": "stack", "channels": ["single", "daily", "console"]},
- "syslog": {"driver": "syslog", "address": "/var/log/system.log"},
- "papertrail": {
- "driver": "syslog",
- "host": "logs.papertrailapp.com",
- "port": None, # specify here the port as an integer
- },
- "slack": {
- "driver": "slack",
- "webhook_url": "",
- },
-}
diff --git a/tests/integrations/config/providers.py b/tests/integrations/config/providers.py
index 7ed36ff9..5905dcce 100644
--- a/tests/integrations/config/providers.py
+++ b/tests/integrations/config/providers.py
@@ -24,7 +24,6 @@
from src.masonite.notification.providers import NotificationProvider
from src.masonite.validation.providers.ValidationProvider import ValidationProvider
from src.masonite.api.providers import ApiProvider
-from src.masonite.logging import LoggingProvider
from ..test_package import MyTestPackageProvider
from tests.integrations.providers import AppProvider
@@ -54,5 +53,4 @@
AppProvider,
ORMProvider,
ApiProvider,
- LoggingProvider,
]
From fc35da51b494baa737188c0cbead2034ad358bb8 Mon Sep 17 00:00:00 2001
From: Eduardo Aguad
Date: Fri, 5 Jul 2024 14:48:11 -0400
Subject: [PATCH 06/27] fix: requirements.txt
---
requirements.txt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 73c7089c..042198d3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,8 +2,8 @@ argon2-cffi
bcrypt>=3.2,<3.3
black
cleo>=0.8.1,<0.9
-cryptography>=3.3.1,<4.0
-dotty_dict>=1.3.0<1.40
+cryptography>=36,<37
+dotty_dict>=1.3.0,<1.40
exceptionite>=2.0,<3
hashids>=1.3,<1.4
hfilesize>=0.1
@@ -20,7 +20,7 @@ python-dotenv>=0.15,<0.16
responses
slackblocks
tldextract>=2.2,<2.3
-werkzeug>=2<3
-watchdog>=2<3
+werkzeug>=2,<3
+watchdog>=2,<3
whitenoise>=5.2,<5.3
pyjwt>=2.3,<2.5
From 9d6adb6597a6bb10841eea6b6a2ce3853d22540d Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Wed, 24 Jul 2024 14:38:48 +0800
Subject: [PATCH 07/27] Fixe Redis Cache driver Issues
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Refactored Redis Cache Driver
- Fixed internal cache not always loaded on first cace access
- added ability to define “timeout” for for item expiry
- - defaults to 1 month. Previous hardcoded value was 10 years
- correctly store and unpack int types
- fix internal cache not removed if store was flushed
- fixed an issue where am imternal cache key would not be updated if it existed, but would be updated in the Redis store
---
src/masonite/cache/drivers/RedisDriver.py | 68 ++++++++++++-----------
1 file changed, 36 insertions(+), 32 deletions(-)
diff --git a/src/masonite/cache/drivers/RedisDriver.py b/src/masonite/cache/drivers/RedisDriver.py
index b49c18c0..1ff473b4 100644
--- a/src/masonite/cache/drivers/RedisDriver.py
+++ b/src/masonite/cache/drivers/RedisDriver.py
@@ -9,13 +9,17 @@ class RedisDriver:
def __init__(self, application):
self.application = application
self.connection = None
- self._internal_cache: "dict|None" = None
+ self.options = None
+ self._internal_cache: dict = None
def set_options(self, options: dict) -> "RedisDriver":
self.options = options
return self
def get_connection(self) -> "Redis":
+ if self.connection:
+ return self.connection
+
try:
from redis import Redis
except ImportError:
@@ -23,40 +27,33 @@ def get_connection(self) -> "Redis":
"Could not find the 'redis' library. Run 'pip install redis' to fix this."
)
- if not self.connection:
- self.connection = Redis(
- **self.options.get("options", {}),
- host=self.options.get("host"),
- port=self.options.get("port"),
- password=self.options.get("password"),
- decode_responses=True,
- )
-
- # populate the internal cache the first time
- # the connection is established
- if self._internal_cache is None and self.connection:
- self._load_from_store(self.connection)
+ self.connection = Redis(
+ **self.options.get("options", {}),
+ host=self.options.get("host"),
+ port=self.options.get("port"),
+ password=self.options.get("password"),
+ decode_responses=True,
+ )
return self.connection
- def _load_from_store(self, connection: "Redis" = None) -> None:
+ def _load_from_store(self) -> None:
"""
copy all the "cache" key value pairs for faster access
"""
- if not connection:
+ if self._internal_cache is not None:
return
- if self._internal_cache is None:
- self._internal_cache = {}
+ self._internal_cache = {}
cursor = "0"
prefix = self.get_cache_namespace()
while cursor != 0:
- cursor, keys = connection.scan(
+ cursor, keys = self.get_connection().scan(
cursor=cursor, match=prefix + "*", count=100000
)
if keys:
- values = connection.mget(*keys)
+ values = self.get_connection().mget(*keys)
store_data = dict(zip(keys, values))
for key, value in store_data.items():
key = key.replace(prefix, "")
@@ -72,15 +69,15 @@ def get_cache_namespace(self) -> str:
return f"{namespace}cache:"
def add(self, key: str, value: Any = None) -> Any:
- if not value:
+ if value is None:
return None
self.put(key, value)
return value
def get(self, key: str, default: Any = None, **options) -> Any:
+ self._load_from_store()
if default and not self.has(key):
- self.put(key, default, **options)
return default
return self._internal_cache.get(key)
@@ -89,20 +86,22 @@ def put(self, key: str, value: Any = None, seconds: int = None, **options) -> An
if not key or value is None:
return None
- time = self.get_expiration_time(seconds)
+ time = seconds or self.get_default_timeout()
store_value = value
if isinstance(value, (dict, list, tuple)):
store_value = json.dumps(value)
+ elif isinstance(value, int):
+ store_value = str(value)
+ self._load_from_store()
self.get_connection().set(
f"{self.get_cache_namespace()}{key}", store_value, ex=time
)
-
- if not self.has(key):
- self._internal_cache.update({key: value})
+ self._internal_cache.update({key: value})
def has(self, key: str) -> bool:
+ self._load_from_store()
return key in self._internal_cache
def increment(self, key: str, amount: int = 1) -> int:
@@ -126,22 +125,27 @@ def remember(self, key: str, callable):
return self.get(key)
def forget(self, key: str) -> None:
+ if not self.has(key):
+ return
self.get_connection().delete(f"{self.get_cache_namespace()}{key}")
self._internal_cache.pop(key)
def flush(self) -> None:
- return self.get_connection().flushall()
+ flushed = self.get_connection().flushall()
+ if flushed:
+ self._internal_cache = None
- def get_expiration_time(self, seconds: int) -> int:
- if seconds is None:
- seconds = 31557600 * 10
+ return flushed
- return seconds
+ def get_default_timeout(self) -> int:
+ # if unset default timeout of cache vars is 1 month
+ return int(self.options.get("timeout", 60 * 60 * 24 * 30))
def unpack_value(self, value: Any) -> Any:
value = str(value)
if value.isdigit():
- return str(value)
+ return int(value)
+
try:
return json.loads(value)
except json.decoder.JSONDecodeError:
From b51c5802cf90418654bfc24de5142ca54e704b9f Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Thu, 25 Jul 2024 12:08:17 +0800
Subject: [PATCH 08/27] Added key expiry to internal cache
Redis has no way to get key ttl enmasse to we oft for an on demand check and update the internal cache as required
---
src/masonite/cache/drivers/RedisDriver.py | 62 ++++++++++++++++++-----
1 file changed, 48 insertions(+), 14 deletions(-)
diff --git a/src/masonite/cache/drivers/RedisDriver.py b/src/masonite/cache/drivers/RedisDriver.py
index 1ff473b4..c22fc52e 100644
--- a/src/masonite/cache/drivers/RedisDriver.py
+++ b/src/masonite/cache/drivers/RedisDriver.py
@@ -1,15 +1,17 @@
import json
from typing import Any, TYPE_CHECKING
+import pendulum as pdlm
if TYPE_CHECKING:
from redis import Redis
+
class RedisDriver:
def __init__(self, application):
self.application = application
self.connection = None
- self.options = None
+ self.options = {}
self._internal_cache: dict = None
def set_options(self, options: dict) -> "RedisDriver":
@@ -58,7 +60,12 @@ def _load_from_store(self) -> None:
for key, value in store_data.items():
key = key.replace(prefix, "")
value = self.unpack_value(value)
- self._internal_cache.setdefault(key, value)
+ # we dont load the ttl (expiry)
+ # because there is an O(N) performance hit
+ self._internal_cache[key] = {
+ "value": value,
+ "expires": None,
+ }
def get_cache_namespace(self) -> str:
"""
@@ -77,17 +84,37 @@ def add(self, key: str, value: Any = None) -> Any:
def get(self, key: str, default: Any = None, **options) -> Any:
self._load_from_store()
- if default and not self.has(key):
- return default
-
- return self._internal_cache.get(key)
+ if not self.has(key):
+ return default or None
+
+ key_expires = self._internal_cache[key].get("expires", None)
+ if key_expires is None:
+ # the ttl value can also provide info on the
+ # existence of the key in the store
+ ttl = self.get_connection().ttl(key)
+ if ttl == -1:
+ # key exists but has no set ttl
+ ttl = self.get_default_timeout()
+ elif ttl == -2:
+ # key not found in store
+ self._internal_cache.pop(key)
+ return default or None
+
+ self._internal_cache[key]["expires"] = self._expires_from_ttl(ttl)
+
+ key_expiry = self._internal_cache[key].get("expires")
+ if pdlm.now() > key_expiry:
+ # the key has expired so remove it from the cache
+ self._internal_cache.pop(key)
+ return default or None
+
+ # the key has not yet expired
+ return self._internal_cache.get(key)["value"]
def put(self, key: str, value: Any = None, seconds: int = None, **options) -> Any:
if not key or value is None:
return None
- time = seconds or self.get_default_timeout()
-
store_value = value
if isinstance(value, (dict, list, tuple)):
store_value = json.dumps(value)
@@ -95,10 +122,17 @@ def put(self, key: str, value: Any = None, seconds: int = None, **options) -> An
store_value = str(value)
self._load_from_store()
+ key_ttl = seconds or self.get_default_timeout()
self.get_connection().set(
- f"{self.get_cache_namespace()}{key}", store_value, ex=time
+ f"{self.get_cache_namespace()}{key}", store_value, ex=key_ttl
)
- self._internal_cache.update({key: value})
+ expires = self._expires_from_ttl(key_ttl)
+ self._internal_cache.update({
+ key: {
+ "value": value,
+ "expires": expires,
+ }
+ })
def has(self, key: str) -> bool:
self._load_from_store()
@@ -132,10 +166,7 @@ def forget(self, key: str) -> None:
def flush(self) -> None:
flushed = self.get_connection().flushall()
- if flushed:
- self._internal_cache = None
-
- return flushed
+ self._internal_cache = None
def get_default_timeout(self) -> int:
# if unset default timeout of cache vars is 1 month
@@ -150,3 +181,6 @@ def unpack_value(self, value: Any) -> Any:
return json.loads(value)
except json.decoder.JSONDecodeError:
return value
+
+ def _expires_from_ttl(self, ttl: int) -> pdlm.DateTime:
+ return pdlm.now().add(seconds=ttl)
From ff621796538cc90d6cf06b1440a4198008f90ca5 Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Thu, 25 Jul 2024 12:09:42 +0800
Subject: [PATCH 09/27] Updated tests for Redis Cache driver
---
tests/features/cache/test_redis_cache.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/tests/features/cache/test_redis_cache.py b/tests/features/cache/test_redis_cache.py
index 61e69c2a..696699f6 100644
--- a/tests/features/cache/test_redis_cache.py
+++ b/tests/features/cache/test_redis_cache.py
@@ -11,7 +11,7 @@ def setUp(self):
self.application.make("cache")
self.driver = self.application.make("cache").store("redis")
- def test_can_add_file_driver(self):
+ def test_can_add_redis_driver(self):
self.assertEqual(self.driver.add("add_key", "value"), "value")
def test_can_get_driver(self):
@@ -23,9 +23,13 @@ def test_can_increment(self):
self.driver.put("count", "1")
self.assertEqual(self.driver.get("count"), "1")
self.driver.increment("count")
- self.assertEqual(self.driver.get("count"), "2")
+ self.assertEqual(self.driver.get("count"), 2)
+ self.driver.increment("count", 3)
+ self.assertEqual(self.driver.get("count"), 5)
self.driver.decrement("count")
- self.assertEqual(self.driver.get("count"), "1")
+ self.assertEqual(self.driver.get("count"), 4)
+ self.driver.decrement("count", 2)
+ self.assertEqual(self.driver.get("count"), 2)
def test_will_not_get_expired(self):
self.driver.put("expire", "1", 1)
From 956753bb0715a4442d66ad21ac543277fc66deab Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Thu, 25 Jul 2024 12:15:29 +0800
Subject: [PATCH 10/27] Fixed linting
---
src/masonite/cache/drivers/RedisDriver.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/masonite/cache/drivers/RedisDriver.py b/src/masonite/cache/drivers/RedisDriver.py
index c22fc52e..37330d80 100644
--- a/src/masonite/cache/drivers/RedisDriver.py
+++ b/src/masonite/cache/drivers/RedisDriver.py
@@ -6,7 +6,6 @@
from redis import Redis
-
class RedisDriver:
def __init__(self, application):
self.application = application
@@ -165,7 +164,7 @@ def forget(self, key: str) -> None:
self._internal_cache.pop(key)
def flush(self) -> None:
- flushed = self.get_connection().flushall()
+ self.get_connection().flushall()
self._internal_cache = None
def get_default_timeout(self) -> int:
From 0d4709d51814180946501c824cf138a54cc2519a Mon Sep 17 00:00:00 2001
From: Kieren Eaton
Date: Thu, 25 Jul 2024 12:45:36 +0800
Subject: [PATCH 11/27] Cleaned up key expiry check
---
src/masonite/cache/drivers/RedisDriver.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/masonite/cache/drivers/RedisDriver.py b/src/masonite/cache/drivers/RedisDriver.py
index 37330d80..5bc760de 100644
--- a/src/masonite/cache/drivers/RedisDriver.py
+++ b/src/masonite/cache/drivers/RedisDriver.py
@@ -86,8 +86,8 @@ def get(self, key: str, default: Any = None, **options) -> Any:
if not self.has(key):
return default or None
- key_expires = self._internal_cache[key].get("expires", None)
- if key_expires is None:
+ key_expiry = self._internal_cache[key].get("expires", None)
+ if key_expiry is None:
# the ttl value can also provide info on the
# existence of the key in the store
ttl = self.get_connection().ttl(key)
@@ -99,9 +99,9 @@ def get(self, key: str, default: Any = None, **options) -> Any:
self._internal_cache.pop(key)
return default or None
- self._internal_cache[key]["expires"] = self._expires_from_ttl(ttl)
+ key_expiry = self._expires_from_ttl(ttl)
+ self._internal_cache[key]["expires"] = key_expiry
- key_expiry = self._internal_cache[key].get("expires")
if pdlm.now() > key_expiry:
# the key has expired so remove it from the cache
self._internal_cache.pop(key)
From 612e1cbbf4f50b180eb4998cd69f8ecaa1a445c2 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Mon, 12 Aug 2024 19:12:30 +0800
Subject: [PATCH 12/27] Added opt-out for cookie encryption
---
src/masonite/cookies/Cookie.py | 2 ++
src/masonite/middleware/route/EncryptCookies.py | 6 ++++++
tests/core/middleware/test_encrypt_cookies.py | 13 +++++++++++++
3 files changed, 21 insertions(+)
diff --git a/src/masonite/cookies/Cookie.py b/src/masonite/cookies/Cookie.py
index 4ba03b61..4290558c 100644
--- a/src/masonite/cookies/Cookie.py
+++ b/src/masonite/cookies/Cookie.py
@@ -9,6 +9,7 @@ def __init__(
timezone=None,
secure=False,
samesite="Strict",
+ encrypt=True,
):
self.name = name
self.value = value
@@ -18,6 +19,7 @@ def __init__(
self.timezone = timezone
self.samesite = samesite
self.path = path
+ self.encrypt = encrypt
def render(self):
response = f"{self.name}={self.value};"
diff --git a/src/masonite/middleware/route/EncryptCookies.py b/src/masonite/middleware/route/EncryptCookies.py
index 6995d31e..8234e50e 100644
--- a/src/masonite/middleware/route/EncryptCookies.py
+++ b/src/masonite/middleware/route/EncryptCookies.py
@@ -4,6 +4,9 @@
class EncryptCookies:
def before(self, request, response):
for _, cookie in request.cookie_jar.all().items():
+ if not cookie.encrypt:
+ continue
+
try:
cookie.value = request.app.make("sign").unsign(cookie.value)
except InvalidToken:
@@ -13,6 +16,9 @@ def before(self, request, response):
def after(self, request, response):
for _, cookie in response.cookie_jar.all().items():
+ if not cookie.encrypt:
+ continue
+
try:
cookie.value = request.app.make("sign").sign(cookie.value)
except InvalidToken:
diff --git a/tests/core/middleware/test_encrypt_cookies.py b/tests/core/middleware/test_encrypt_cookies.py
index 2f9014e4..5ceb2c81 100644
--- a/tests/core/middleware/test_encrypt_cookies.py
+++ b/tests/core/middleware/test_encrypt_cookies.py
@@ -15,3 +15,16 @@ def test_encrypts_cookies(self):
response.cookie("test", "value")
EncryptCookies().after(request, response)
self.assertNotEqual(response.cookie("test"), "value")
+
+def test_encrypt_cookies_opt_out(self):
+ request = self.make_request(
+ {"HTTP_COOKIE": f"test_key=test value"}
+ )
+
+ response = self.make_response()
+ EncryptCookies().before(request, None)
+ self.assertEqual(request.cookie("test_key", encrypt=False), "test value")
+
+ response.cookie("test", "value")
+ EncryptCookies().after(request, response)
+ self.assertNotEqual(response.cookie("test_key", encrypt=False), "test value")
From 3ad49731466ddd85338362fb783ddf8b97e22994 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Mon, 12 Aug 2024 19:19:48 +0800
Subject: [PATCH 13/27] Fixed test not in class
---
tests/core/middleware/test_encrypt_cookies.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/core/middleware/test_encrypt_cookies.py b/tests/core/middleware/test_encrypt_cookies.py
index 5ceb2c81..909ac14d 100644
--- a/tests/core/middleware/test_encrypt_cookies.py
+++ b/tests/core/middleware/test_encrypt_cookies.py
@@ -16,7 +16,7 @@ def test_encrypts_cookies(self):
EncryptCookies().after(request, response)
self.assertNotEqual(response.cookie("test"), "value")
-def test_encrypt_cookies_opt_out(self):
+ def test_encrypt_cookies_opt_out(self):
request = self.make_request(
{"HTTP_COOKIE": f"test_key=test value"}
)
From 77d70c99fd22a8cbf011065ace1b293b03a3189c Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Sun, 18 Aug 2024 16:46:18 -0400
Subject: [PATCH 14/27] removed python badge
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ffdef588..e76d616e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
+
From 9b62e9e1408cdef55c8d4909df62e9060489382e Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Sun, 18 Aug 2024 16:46:48 -0400
Subject: [PATCH 15/27] fixed readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e76d616e..72c75c99 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ Have questions or want to talk? Be sure to join the [Masonite Discord Community]
## Getting Started Quickly
-Create and activate a virtual environment and if you have a working Python 3.7+ installation then getting started is as quick as typing
+Create and activate a virtual environment and if you have a working Python <= 3.11 installation then getting started is as quick as typing
```bash
pip install masonite
From 4892c1c1098bf71df0adf8331ae842c1985a6fe9 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Thu, 22 Aug 2024 11:57:32 +0800
Subject: [PATCH 16/27] Fixed errors key missing from session
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Added the errors key back into the session flash.
Changed the View errors helper name from ‘bag’ back to ‘errors’
---
src/masonite/middleware/route/SessionMiddleware.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/masonite/middleware/route/SessionMiddleware.py b/src/masonite/middleware/route/SessionMiddleware.py
index 8d9a0b41..5a1103bf 100644
--- a/src/masonite/middleware/route/SessionMiddleware.py
+++ b/src/masonite/middleware/route/SessionMiddleware.py
@@ -17,7 +17,11 @@ def before(self, request, response):
request.app.make("request").session = Session
# TODO: Remove in Masonite 5
- request.app.make("view").share({"bag": MessageBag(Session.get("errors") or {}).helper})
+ bag = MessageBag(Session.get("errors") or {})
+ request.app.make("view").share({"errors": bag.helper})
+ # errors are stored in session flash so 'getting' them actually clears them
+ # so re-add them to the session as a MessageBag
+ Session.flash('errors', bag)
return request
def after(self, request, _):
From 51ceb160e15191d003260512167357f451370280 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Thu, 22 Aug 2024 12:16:05 +0800
Subject: [PATCH 17/27] Only add errors to the session if they exist
---
src/masonite/middleware/route/SessionMiddleware.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/masonite/middleware/route/SessionMiddleware.py b/src/masonite/middleware/route/SessionMiddleware.py
index 5a1103bf..bf0f4e2b 100644
--- a/src/masonite/middleware/route/SessionMiddleware.py
+++ b/src/masonite/middleware/route/SessionMiddleware.py
@@ -17,11 +17,12 @@ def before(self, request, response):
request.app.make("request").session = Session
# TODO: Remove in Masonite 5
- bag = MessageBag(Session.get("errors") or {})
- request.app.make("view").share({"errors": bag.helper})
+ errors = Session.get("errors") or {}
+ request.app.make("view").share({"errors": MessageBag(errors).helper})
# errors are stored in session flash so 'getting' them actually clears them
- # so re-add them to the session as a MessageBag
- Session.flash('errors', bag)
+ # if any then re-add them to the session
+ if errors:
+ Session.flash('errors', errors)
return request
def after(self, request, _):
From 47038681c47e5eb7f3fd13416ba2b8fc9e6bbedf Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Thu, 22 Aug 2024 18:52:32 +0800
Subject: [PATCH 18/27] upadted session re-add for uniformity accross modules
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Other modules will probably not expect a MessageBag as the content of the session ‘errors’ key.
So just pass it through
---
src/masonite/middleware/route/SessionMiddleware.py | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/masonite/middleware/route/SessionMiddleware.py b/src/masonite/middleware/route/SessionMiddleware.py
index bf0f4e2b..5e0a02af 100644
--- a/src/masonite/middleware/route/SessionMiddleware.py
+++ b/src/masonite/middleware/route/SessionMiddleware.py
@@ -16,13 +16,15 @@ def before(self, request, response):
request.app.make("response").with_success = self.with_success
request.app.make("request").session = Session
- # TODO: Remove in Masonite 5
- errors = Session.get("errors") or {}
- request.app.make("view").share({"errors": MessageBag(errors).helper})
- # errors are stored in session flash so 'getting' them actually clears them
- # if any then re-add them to the session
+ # TODO: Check this in Masonite 5
+ # errors are stored in session flash so 'getting' them
+ # actually clears them out of the session
+ errors = Session.get("errors")
+ request.app.make("view").share({"errors": MessageBag(errors or {}).helper})
+ # if any errors then re-add them to the session
if errors:
- Session.flash('errors', errors)
+ Session.flash("errors", errors)
+
return request
def after(self, request, _):
From 978eb1d9751fdf91ff1c431ee268ef25dac78f8a Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Sun, 25 Aug 2024 06:09:47 +0800
Subject: [PATCH 19/27] Added tests using SessionMiddleware
---
tests/core/response/test_response_helpers.py | 33 +++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tests/core/response/test_response_helpers.py b/tests/core/response/test_response_helpers.py
index a652d903..f3479f24 100644
--- a/tests/core/response/test_response_helpers.py
+++ b/tests/core/response/test_response_helpers.py
@@ -1,5 +1,6 @@
-from tests import TestCase
+from src.masonite.middleware import SessionMiddleware
from src.masonite.routes import Route
+from tests import TestCase
class TestResponseHelpers(TestCase):
@@ -17,3 +18,33 @@ def test_with_errors(self):
self.get("/test-with-errors").assertSessionHasErrors().assertSessionHasErrors(
["email"]
)
+
+
+class TestResponseWithMiddleware(TestCase):
+ def setUp(self):
+ super().setUp()
+ self.application.make("middleware").add(
+ {
+ "web": SessionMiddleware,
+ }
+ )
+ self.addRoutes(
+ Route.get("/test-with-errors", "WelcomeController@with_errors").middleware(
+ "web"
+ ),
+ Route.get(
+ "/test-with-input", "WelcomeController@form_with_input"
+ ).middleware("web"),
+ )
+
+ def test_with_input(self):
+ (
+ self.get("/test-with-input", {"name": "Sam"})
+ .assertSessionHas("name", "Sam")
+ .assertSessionHasNoErrors()
+ )
+
+ def test_with_errors(self):
+ self.get("/test-with-errors").assertSessionHasErrors().assertSessionHasErrors(
+ ["email"]
+ )
From d0e26acd595942c39d106db94d203683779118f7 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Fri, 30 Aug 2024 12:37:30 +0800
Subject: [PATCH 20/27] Added test to confirm no errors after getting them
---
tests/core/response/test_response_helpers.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tests/core/response/test_response_helpers.py b/tests/core/response/test_response_helpers.py
index f3479f24..76b06650 100644
--- a/tests/core/response/test_response_helpers.py
+++ b/tests/core/response/test_response_helpers.py
@@ -45,6 +45,9 @@ def test_with_input(self):
)
def test_with_errors(self):
- self.get("/test-with-errors").assertSessionHasErrors().assertSessionHasErrors(
- ["email"]
+ (
+ self.get("/test-with-errors")
+ .assertSessionHasErrors()
+ .assertSessionHasErrors(["email"])
+ .assertSessionHasNoErrors()
)
From f7090649ecdf0d30f77f0f6b2c03c4e27fde6bd0 Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Wed, 11 Sep 2024 07:19:14 +0800
Subject: [PATCH 21/27] remove masonite as a requirement of itself
---
requirements.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index 042198d3..0cde7963 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,7 +11,6 @@ hupper>=1.10,<1.11
inflection>=0.3,<0.4
jinja2>=3.0.0,<3.1
masonite-orm>=2,<3
-masonite>=4,<5
pendulum>=2,<3
pwnedapi
vonage
From b0aa6304b2c11dda8cbafda97f7d07902d8e7dfe Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Wed, 11 Sep 2024 07:24:05 +0800
Subject: [PATCH 22/27] updated dependencies
this covers many CVE fixes
---
requirements.txt | 6 +++---
setup.py | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 0cde7963..c4392aa1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ hashids>=1.3,<1.4
hfilesize>=0.1
hupper>=1.10,<1.11
inflection>=0.3,<0.4
-jinja2>=3.0.0,<3.1
+jinja2<3.2
masonite-orm>=2,<3
pendulum>=2,<3
pwnedapi
@@ -19,7 +19,7 @@ python-dotenv>=0.15,<0.16
responses
slackblocks
tldextract>=2.2,<2.3
-werkzeug>=2,<3
+werkzeug>=3,<4
watchdog>=2,<3
whitenoise>=5.2,<5.3
-pyjwt>=2.3,<2.5
+pyjwt>=2.4,<2.5
diff --git a/setup.py b/setup.py
index 4ec9aa4f..b5a77a49 100644
--- a/setup.py
+++ b/setup.py
@@ -42,7 +42,7 @@
"inflection>=0.3,<0.4",
"exceptionite>=2.2,<3.0",
"pendulum>=2,<3",
- "jinja2<3.1.0",
+ "jinja2<3.2",
"cleo>=0.8.1,<0.9",
"hupper>=1.10,<1.11",
"bcrypt>=3.2,<3.3",
@@ -54,9 +54,9 @@
"tldextract>=2.2,<2.3",
"hfilesize>=0.1",
"dotty_dict>=1.3.0,<1.40",
- "pyjwt>=2.3,<2.5",
+ "pyjwt>=2.4,<2.5",
"pytest>=7,<8",
- "werkzeug>=2,<3",
+ "werkzeug>=3,<4",
"watchdog>=2,<=4",
],
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
From 5492f1117fa2548880a6feeb75142122f03acd3a Mon Sep 17 00:00:00 2001
From: Kieren Eaton <499977+circulon@users.noreply.github.com>
Date: Wed, 11 Sep 2024 08:00:52 +0800
Subject: [PATCH 23/27] fixed werkzeug not installing with python < 3.8
---
requirements.txt | 3 ++-
setup.py | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index c4392aa1..3f0d139a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,7 +19,8 @@ python-dotenv>=0.15,<0.16
responses
slackblocks
tldextract>=2.2,<2.3
-werkzeug>=3,<4
+werkzeug>=2,<3; python_version < '3.8'
+werkzeug>=3,<4; python_version >= '3.8'
watchdog>=2,<3
whitenoise>=5.2,<5.3
pyjwt>=2.4,<2.5
diff --git a/setup.py b/setup.py
index b5a77a49..ae8eb6a9 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,8 @@
"dotty_dict>=1.3.0,<1.40",
"pyjwt>=2.4,<2.5",
"pytest>=7,<8",
- "werkzeug>=3,<4",
+ "werkzeug>=2,<3; python_version < '3.8'",
+ "werkzeug>=3,<4; python_version >= '3.8'",
"watchdog>=2,<=4",
],
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
From bf67d7c9e93bc484ffad8f6ea769f7782db2075e Mon Sep 17 00:00:00 2001
From: Joseph Mancuso
Date: Wed, 16 Oct 2024 15:53:33 -0400
Subject: [PATCH 24/27] Update __init__.py
---
src/masonite/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/masonite/__init__.py b/src/masonite/__init__.py
index 9abbe5d7..caa48dd8 100644
--- a/src/masonite/__init__.py
+++ b/src/masonite/__init__.py
@@ -1 +1 @@
-__version__ = "4.20.0"
+__version__ = "4.20.1"
From 104300ca910f9d62df4f830dbe360a78282fa390 Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Wed, 16 Oct 2024 16:10:51 -0400
Subject: [PATCH 25/27] Refactor old() helper to include a default option
---
src/masonite/sessions/helpers.py | 6 +++---
tests/core/request/test_http_requests.py | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/masonite/sessions/helpers.py b/src/masonite/sessions/helpers.py
index 49498433..a9f04295 100644
--- a/src/masonite/sessions/helpers.py
+++ b/src/masonite/sessions/helpers.py
@@ -1,6 +1,6 @@
from ..facades import Session
-def old(key: str) -> str:
- """Helper used to get value from session for the given key."""
- return Session.get(key) or ""
+def old(key: str, default: str = "") -> str:
+ """Helper used to get value from session for the given key, with a default option."""
+ return Session.get(key) or default
diff --git a/tests/core/request/test_http_requests.py b/tests/core/request/test_http_requests.py
index cb3d74b5..0801f31b 100644
--- a/tests/core/request/test_http_requests.py
+++ b/tests/core/request/test_http_requests.py
@@ -47,7 +47,7 @@ def test_server_error_with_debug_on(self):
def test_old_helper(self):
self.get("/test-with-input", {"name": "Sam"})
assert self.application.make("request").old("name") == "Sam"
- assert self.application.make("request").old("wrong-input") == ""
+ assert self.application.make("request").old("wrong-input", "default") == "default"
def test_get_route(self):
self.get("/")
From 3df7e13df453f50c669665a6ff87c95fbb9c28c9 Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Wed, 16 Oct 2024 16:15:23 -0400
Subject: [PATCH 26/27] Refactor import statements in Cookie.py and request.py
---
src/masonite/cookies/Cookie.py | 2 +-
src/masonite/request/request.py | 4 ++--
tests/features/validation/test_request_validation.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/masonite/cookies/Cookie.py b/src/masonite/cookies/Cookie.py
index 2743179a..9cb3a540 100644
--- a/src/masonite/cookies/Cookie.py
+++ b/src/masonite/cookies/Cookie.py
@@ -1,4 +1,4 @@
-from masonite.configuration import config
+from ..configuration import config
class Cookie:
diff --git a/src/masonite/request/request.py b/src/masonite/request/request.py
index 6636650d..6a38940f 100644
--- a/src/masonite/request/request.py
+++ b/src/masonite/request/request.py
@@ -113,9 +113,9 @@ def only(self, *inputs: str) -> dict:
"""Pass arguments as string arguments such as request.only("arg1", "arg2") to get back a dictionary of only those inputs."""
return self.input_bag.only(*inputs)
- def old(self, key: str):
+ def old(self, key: str, default: str = "") -> str:
"""Get value from session for the given key."""
- return old_helper(key)
+ return old_helper(key, default)
def is_not_safe(self) -> bool:
"""Check if the current request is considered 'safe', meaning that the request method is
diff --git a/tests/features/validation/test_request_validation.py b/tests/features/validation/test_request_validation.py
index 02259c61..3af415aa 100644
--- a/tests/features/validation/test_request_validation.py
+++ b/tests/features/validation/test_request_validation.py
@@ -1,5 +1,5 @@
from tests import TestCase
-from masonite.validation import email, isnt, is_in, numeric
+from src.masonite.validation import email, isnt, is_in, numeric
from src.masonite.validation import Validator
From 5271c3c05c917816d97c14453b7cce47d8d88c4f Mon Sep 17 00:00:00 2001
From: Joe Mancuso
Date: Wed, 16 Oct 2024 16:29:04 -0400
Subject: [PATCH 27/27] Refactor Route.py to include a default option for route
names
---
src/masonite/routes/Route.py | 2 +-
tests/routes/test_routes.py | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/masonite/routes/Route.py b/src/masonite/routes/Route.py
index 5ee2bdb1..252727d8 100644
--- a/src/masonite/routes/Route.py
+++ b/src/masonite/routes/Route.py
@@ -157,7 +157,7 @@ def group(self, *routes, **options):
route.compile_route_to_regex()
if options.get("name"):
- route._name = options.get("name") + route._name
+ route._name = options.get("name", "") + (route._name or "")
if options.get("domain"):
route.domain(options.get("domain"))
diff --git a/tests/routes/test_routes.py b/tests/routes/test_routes.py
index 2b6bd3d7..10fae589 100644
--- a/tests/routes/test_routes.py
+++ b/tests/routes/test_routes.py
@@ -132,12 +132,14 @@ def test_group_naming(self):
Route.group(
Route.get("/group", "WelcomeController@show").name(".index"),
Route.post("/login", "WelcomeController@show").name(".index"),
+ Route.post("/login", "WelcomeController@show"),
prefix="/testing",
name="dashboard",
)
)
route = router.find_by_name("dashboard.index")
+ route = router.find_by_name("dashboard")
self.assertTrue(route)
def test_compile_year(self):