Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4 to v5 #825

Merged
merged 44 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7dd21d9
Merge pull request #794 from eaguad1337/4.0
josephmancuso Apr 4, 2024
035be30
Fixed false pass if regex meta chars used
circulon May 3, 2024
dc36c9a
cookie settings from config
riteshjha May 19, 2024
324ed72
fixed linting issue
riteshjha Jun 16, 2024
17e47d0
added object check
josephmancuso Jun 22, 2024
cc1411d
Merge pull request #803 from MasoniteFramework/hotfix/database-queue-…
josephmancuso Jun 22, 2024
8e83cf7
Merge pull request #718 from girardinsamuel/feat/663
eaguad1337 Jul 5, 2024
09f681d
Revert "[5.X] Add `logging` feature"
eaguad1337 Jul 5, 2024
4b8b009
Merge pull request #807 from MasoniteFramework/revert-718-feat/663
eaguad1337 Jul 5, 2024
22c5512
Merge pull request #799 from circulon/fix/strong_validator_special_chars
josephmancuso Jul 5, 2024
fc35da5
fix: requirements.txt
eaguad1337 Jul 5, 2024
9d6adb6
Fixe Redis Cache driver Issues
circulon Jul 24, 2024
9d26aec
Merge pull request #810 from MasoniteFramework/fix/requirements
josephmancuso Jul 24, 2024
335db24
Merge branch '4.0' of https://github.com/MasoniteFramework/masonite i…
circulon Jul 24, 2024
b51c580
Added key expiry to internal cache
circulon Jul 25, 2024
ff62179
Updated tests for Redis Cache driver
circulon Jul 25, 2024
956753b
Fixed linting
circulon Jul 25, 2024
0d4709d
Cleaned up key expiry check
circulon Jul 25, 2024
612e1cb
Added opt-out for cookie encryption
circulon Aug 12, 2024
3ad4973
Fixed test not in class
circulon Aug 12, 2024
77a5499
Merge pull request #811 from circulon/fix/redis_cache_issues
josephmancuso Aug 13, 2024
bd03d85
Merge pull request #814 from circulon/feature/cookie_encrypt_opt_out
josephmancuso Aug 13, 2024
51605ae
Merge branch '4.0' into feature/800
josephmancuso Aug 13, 2024
80c738b
Merge pull request #801 from riteshjha/feature/800
josephmancuso Aug 13, 2024
77d70c9
removed python badge
josephmancuso Aug 18, 2024
9b62e9e
fixed readme
josephmancuso Aug 18, 2024
4892c1c
Fixed errors key missing from session
circulon Aug 22, 2024
51ceb16
Only add errors to the session if they exist
circulon Aug 22, 2024
4703868
upadted session re-add for uniformity accross modules
circulon Aug 22, 2024
978eb1d
Added tests using SessionMiddleware
circulon Aug 24, 2024
d0e26ac
Added test to confirm no errors after getting them
circulon Aug 30, 2024
f709064
remove masonite as a requirement of itself
circulon Sep 10, 2024
b0aa630
updated dependencies
circulon Sep 10, 2024
5492f11
fixed werkzeug not installing with python < 3.8
circulon Sep 11, 2024
30c6863
Merge branch '4.0' of https://github.com/MasoniteFramework/masonite i…
josephmancuso Sep 13, 2024
23d13ab
Merge pull request #819 from circulon/feature/update_deps
josephmancuso Oct 16, 2024
e12a938
Merge pull request #817 from circulon/fix/errors_always_empty
josephmancuso Oct 16, 2024
bf67d7c
Update __init__.py
josephmancuso Oct 16, 2024
c659b3e
Merge branch '4.0' of https://github.com/MasoniteFramework/masonite i…
josephmancuso Oct 16, 2024
104300c
Refactor old() helper to include a default option
josephmancuso Oct 16, 2024
3df7e13
Refactor import statements in Cookie.py and request.py
josephmancuso Oct 16, 2024
70b4537
Merge pull request #822 from MasoniteFramework/feature/748
josephmancuso Oct 16, 2024
5271c3c
Refactor Route.py to include a default option for route names
josephmancuso Oct 16, 2024
3c3c73f
Merge pull request #823 from MasoniteFramework/feature/745
josephmancuso Oct 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ src/masonite.egg-info/*
.vscode
build/
venv4
.python-version
*.whl
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
</p>
<p align="center">
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/MasoniteFramework/masonite/pythonapp.yml?branch=develop">
<img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="Python Version">

<img alt="GitHub release (latest by date including pre-releases)" src="https://img.shields.io/github/v/release/MasoniteFramework/masonite?include_prereleases">
<img src="https://img.shields.io/github/license/MasoniteFramework/masonite.svg" alt="License">
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
Expand Down Expand Up @@ -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
Expand Down
910 changes: 0 additions & 910 deletions poetry.lock

This file was deleted.

35 changes: 0 additions & 35 deletions pyproject.toml

This file was deleted.

8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ 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
masonite>=4,<5
pendulum>=2,<3
pwnedapi
vonage
Expand All @@ -20,7 +19,8 @@ python-dotenv>=0.15,<0.16
responses
slackblocks
tldextract>=2.2,<2.3
werkzeug>=2,<3
werkzeug>=2,<3; python_version < '3.8'
werkzeug>=3,<4; python_version >= '3.8'
watchdog>=2,<3
whitenoise>=5.2,<5.3
pyjwt>=2.3,<2.5
pyjwt>=2.4,<2.5
11 changes: 5 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -54,10 +54,11 @@
"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",
"watchdog>=2,<3",
"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
classifiers=[
Expand Down Expand Up @@ -143,8 +144,6 @@
"masonite.helpers",
"masonite.input",
"masonite.loader",
"masonite.logging",
"masonite.logging.drivers",
"masonite.mail.drivers",
"masonite.mail",
"masonite.middleware.route",
Expand Down
2 changes: 1 addition & 1 deletion src/masonite/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "4.19.0"
__version__ = "4.20.1"
2 changes: 2 additions & 0 deletions src/masonite/cache/drivers/FileDriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def remember(self, key, callable):

callable(self)

return self.get(key)

def forget(self, key):
try:
os.remove(os.path.join(self._get_directory(), key))
Expand Down
2 changes: 2 additions & 0 deletions src/masonite/cache/drivers/MemcacheDriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def remember(self, key, callable):

callable(self)

return self.get(key)

def forget(self, key):
return self.get_connection().delete(f"{self.get_name()}_cache_{key}")

Expand Down
119 changes: 79 additions & 40 deletions src/masonite/cache/drivers/RedisDriver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from typing import Any, TYPE_CHECKING

import pendulum as pdlm
if TYPE_CHECKING:
from redis import Redis

Expand All @@ -9,59 +10,61 @@ class RedisDriver:
def __init__(self, application):
self.application = application
self.connection = None
self._internal_cache: "dict|None" = None
self.options = {}
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:
raise ModuleNotFoundError(
"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, "")
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:
"""
Expand All @@ -72,37 +75,66 @@ 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:
if default and not self.has(key):
self.put(key, default, **options)
return default

return self._internal_cache.get(key)
self._load_from_store()
if not self.has(key):
return default or 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)
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

key_expiry = self._expires_from_ttl(ttl)
self._internal_cache[key]["expires"] = key_expiry

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 = self.get_expiration_time(seconds)

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()
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
)

if not self.has(key):
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()
return key in self._internal_cache

def increment(self, key: str, amount: int = 1) -> int:
Expand All @@ -123,24 +155,31 @@ def remember(self, key: str, callable):

callable(self)

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()
self.get_connection().flushall()
self._internal_cache = None

def get_expiration_time(self, seconds: int) -> int:
if seconds is None:
seconds = 31557600 * 10

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:
return value

def _expires_from_ttl(self, ttl: int) -> pdlm.DateTime:
return pdlm.now().add(seconds=ttl)
Loading
Loading