[REF-1464] Handle requirements.txt encoding (#2284)
This commit is contained in:
parent
e245b6b6a0
commit
9da65b9a9a
4
poetry.lock
generated
4
poetry.lock
generated
@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alembic"
|
name = "alembic"
|
||||||
@ -2473,4 +2473,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "3b45d79329803bc24d8874a6da7af8f6eb6c4677eb100cbe300ad6f9e4bc8c69"
|
content-hash = "c22317cf6beac82268e73619e984788895d258bb7b7983eacdfbf6093c419dc3"
|
||||||
|
@ -58,6 +58,7 @@ wrapt = [
|
|||||||
packaging = "^23.1"
|
packaging = "^23.1"
|
||||||
pipdeptree = "^2.13.0"
|
pipdeptree = "^2.13.0"
|
||||||
reflex-hosting-cli = ">=0.1.2"
|
reflex-hosting-cli = ">=0.1.2"
|
||||||
|
charset-normalizer = "^3.3.2"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = "^7.1.2"
|
pytest = "^7.1.2"
|
||||||
|
@ -47,7 +47,7 @@ class RequirementsTxt(SimpleNamespace):
|
|||||||
# The requirements.txt file.
|
# The requirements.txt file.
|
||||||
FILE = "requirements.txt"
|
FILE = "requirements.txt"
|
||||||
# The partial text used to form requirement that pins a reflex version
|
# The partial text used to form requirement that pins a reflex version
|
||||||
DEFAULTS_STUB = f"{Reflex.MODULE_NAME}>="
|
DEFAULTS_STUB = f"{Reflex.MODULE_NAME}=="
|
||||||
|
|
||||||
|
|
||||||
# The deployment URL.
|
# The deployment URL.
|
||||||
|
@ -159,7 +159,6 @@ def get_app(reload: bool = False) -> ModuleType:
|
|||||||
sys.path.insert(0, os.getcwd())
|
sys.path.insert(0, os.getcwd())
|
||||||
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
||||||
if reload:
|
if reload:
|
||||||
|
|
||||||
importlib.reload(app)
|
importlib.reload(app)
|
||||||
return app
|
return app
|
||||||
|
|
||||||
@ -263,18 +262,33 @@ def initialize_requirements_txt():
|
|||||||
the requirements.txt file.
|
the requirements.txt file.
|
||||||
"""
|
"""
|
||||||
fp = Path(constants.RequirementsTxt.FILE)
|
fp = Path(constants.RequirementsTxt.FILE)
|
||||||
fp.touch(exist_ok=True)
|
encoding = "utf-8"
|
||||||
|
if not fp.exists():
|
||||||
|
fp.touch()
|
||||||
|
else:
|
||||||
|
# Detect the encoding of the original file
|
||||||
|
import charset_normalizer
|
||||||
|
|
||||||
|
charset_matches = charset_normalizer.from_path(fp)
|
||||||
|
maybe_charset_match = charset_matches.best()
|
||||||
|
if maybe_charset_match is None:
|
||||||
|
console.debug(f"Unable to detect encoding for {fp}, exiting.")
|
||||||
|
return
|
||||||
|
encoding = maybe_charset_match.encoding
|
||||||
|
console.debug(f"Detected encoding for {fp} as {encoding}.")
|
||||||
try:
|
try:
|
||||||
with open(fp, "r") as f:
|
other_requirements_exist = False
|
||||||
|
with open(fp, "r", encoding=encoding) as f:
|
||||||
for req in f.readlines():
|
for req in f.readlines():
|
||||||
# Check if we have a package name that is reflex
|
# Check if we have a package name that is reflex
|
||||||
if re.match(r"^reflex[^a-zA-Z0-9]", req):
|
if re.match(r"^reflex[^a-zA-Z0-9]", req):
|
||||||
console.debug(f"{fp} already has reflex as dependency.")
|
console.debug(f"{fp} already has reflex as dependency.")
|
||||||
return
|
return
|
||||||
with open(fp, "a") as f:
|
other_requirements_exist = True
|
||||||
|
with open(fp, "a", encoding=encoding) as f:
|
||||||
|
preceding_newline = "\n" if other_requirements_exist else ""
|
||||||
f.write(
|
f.write(
|
||||||
f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
f"{preceding_newline}{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
console.info(f"Unable to check {fp} for reflex dependency.")
|
console.info(f"Unable to check {fp} for reflex dependency.")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from unittest.mock import mock_open
|
from unittest.mock import Mock, mock_open
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -56,24 +56,37 @@ def test_update_next_config(config, export, expected_output):
|
|||||||
assert output == expected_output
|
assert output == expected_output
|
||||||
|
|
||||||
|
|
||||||
def test_initialize_requirements_txt(mocker):
|
def test_initialize_requirements_txt_no_op(mocker):
|
||||||
# File exists, reflex is included, do nothing
|
# File exists, reflex is included, do nothing
|
||||||
mocker.patch("os.path.exists", return_value=True)
|
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||||
|
mocker.patch(
|
||||||
|
"charset_normalizer.from_path",
|
||||||
|
return_value=Mock(best=lambda: Mock(encoding="utf-8")),
|
||||||
|
)
|
||||||
|
mock_fp_touch = mocker.patch("pathlib.Path.touch")
|
||||||
open_mock = mock_open(read_data="reflex==0.2.9")
|
open_mock = mock_open(read_data="reflex==0.2.9")
|
||||||
mocker.patch("builtins.open", open_mock)
|
mocker.patch("builtins.open", open_mock)
|
||||||
initialize_requirements_txt()
|
initialize_requirements_txt()
|
||||||
assert open_mock.call_count == 1
|
assert open_mock.call_count == 1
|
||||||
|
assert open_mock.call_args.kwargs["encoding"] == "utf-8"
|
||||||
assert open_mock().write.call_count == 0
|
assert open_mock().write.call_count == 0
|
||||||
|
mock_fp_touch.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_initialize_requirements_txt_missing_reflex(mocker):
|
def test_initialize_requirements_txt_missing_reflex(mocker):
|
||||||
# File exists, reflex is not included, add reflex
|
# File exists, reflex is not included, add reflex
|
||||||
|
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||||
|
mocker.patch(
|
||||||
|
"charset_normalizer.from_path",
|
||||||
|
return_value=Mock(best=lambda: Mock(encoding="utf-8")),
|
||||||
|
)
|
||||||
open_mock = mock_open(read_data="random-package=1.2.3")
|
open_mock = mock_open(read_data="random-package=1.2.3")
|
||||||
mocker.patch("builtins.open", open_mock)
|
mocker.patch("builtins.open", open_mock)
|
||||||
initialize_requirements_txt()
|
initialize_requirements_txt()
|
||||||
# Currently open for read, then open for append
|
# Currently open for read, then open for append
|
||||||
assert open_mock.call_count == 2
|
assert open_mock.call_count == 2
|
||||||
assert open_mock().write.call_count == 1
|
for call_args in open_mock.call_args_list:
|
||||||
|
assert call_args.kwargs["encoding"] == "utf-8"
|
||||||
assert (
|
assert (
|
||||||
open_mock().write.call_args[0][0]
|
open_mock().write.call_args[0][0]
|
||||||
== f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
== f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
||||||
@ -82,12 +95,46 @@ def test_initialize_requirements_txt_missing_reflex(mocker):
|
|||||||
|
|
||||||
def test_initialize_requirements_txt_not_exist(mocker):
|
def test_initialize_requirements_txt_not_exist(mocker):
|
||||||
# File does not exist, create file with reflex
|
# File does not exist, create file with reflex
|
||||||
mocker.patch("os.path.exists", return_value=False)
|
mocker.patch("pathlib.Path.exists", return_value=False)
|
||||||
open_mock = mock_open()
|
open_mock = mock_open()
|
||||||
mocker.patch("builtins.open", open_mock)
|
mocker.patch("builtins.open", open_mock)
|
||||||
initialize_requirements_txt()
|
initialize_requirements_txt()
|
||||||
assert open_mock.call_count == 2
|
assert open_mock.call_count == 2
|
||||||
|
# By default, use utf-8 encoding
|
||||||
|
for call_args in open_mock.call_args_list:
|
||||||
|
assert call_args.kwargs["encoding"] == "utf-8"
|
||||||
assert open_mock().write.call_count == 1
|
assert open_mock().write.call_count == 1
|
||||||
|
assert (
|
||||||
|
open_mock().write.call_args[0][0]
|
||||||
|
== f"{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_requirements_txt_cannot_detect_encoding(mocker):
|
||||||
|
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||||
|
mock_open = mocker.patch("builtins.open")
|
||||||
|
mocker.patch(
|
||||||
|
"charset_normalizer.from_path",
|
||||||
|
return_value=Mock(best=lambda: None),
|
||||||
|
)
|
||||||
|
initialize_requirements_txt()
|
||||||
|
mock_open.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_requirements_txt_other_encoding(mocker):
|
||||||
|
mocker.patch("pathlib.Path.exists", return_value=True)
|
||||||
|
mocker.patch(
|
||||||
|
"charset_normalizer.from_path",
|
||||||
|
return_value=Mock(best=lambda: Mock(encoding="utf-16")),
|
||||||
|
)
|
||||||
|
initialize_requirements_txt()
|
||||||
|
open_mock = mock_open(read_data="random-package=1.2.3")
|
||||||
|
mocker.patch("builtins.open", open_mock)
|
||||||
|
initialize_requirements_txt()
|
||||||
|
# Currently open for read, then open for append
|
||||||
|
assert open_mock.call_count == 2
|
||||||
|
for call_args in open_mock.call_args_list:
|
||||||
|
assert call_args.kwargs["encoding"] == "utf-16"
|
||||||
assert (
|
assert (
|
||||||
open_mock().write.call_args[0][0]
|
open_mock().write.call_args[0][0]
|
||||||
== f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
== f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user