|
2 | 2 |
|
3 | 3 | import json |
4 | 4 | import random |
5 | | -import re |
6 | 5 | from pathlib import Path |
7 | 6 |
|
8 | 7 | from reflex_base import constants |
@@ -42,44 +41,101 @@ def initialize_gitignore( |
42 | 41 | gitignore_file.write_text("\n".join(files_to_ignore) + "\n") |
43 | 42 |
|
44 | 43 |
|
45 | | -def initialize_requirements_txt() -> bool: |
| 44 | +def _read_dependency_file(file_path: Path) -> tuple[str | None, str | None]: |
| 45 | + """Read a dependency file with a forgiving encoding strategy. |
| 46 | +
|
| 47 | + Args: |
| 48 | + file_path: The file to read. |
| 49 | +
|
| 50 | + Returns: |
| 51 | + A tuple of file content and the encoding used to read it. |
| 52 | + """ |
| 53 | + try: |
| 54 | + return file_path.read_text(), None |
| 55 | + except UnicodeDecodeError: |
| 56 | + pass |
| 57 | + except Exception as e: |
| 58 | + console.error(f"Failed to read {file_path} due to {e}.") |
| 59 | + raise SystemExit(1) from None |
| 60 | + |
| 61 | + try: |
| 62 | + return file_path.read_text(encoding="utf-8"), "utf-8" |
| 63 | + except UnicodeDecodeError: |
| 64 | + return None, None |
| 65 | + except Exception as e: |
| 66 | + console.error(f"Failed to read {file_path} due to {e}.") |
| 67 | + raise SystemExit(1) from None |
| 68 | + |
| 69 | + |
| 70 | +def _has_reflex_requirement_line(requirements_text: str) -> bool: |
| 71 | + """Check whether requirements.txt already contains reflex. |
| 72 | +
|
| 73 | + Returns: |
| 74 | + Whether reflex is already present in the requirements text. |
| 75 | + """ |
| 76 | + return any( |
| 77 | + _is_reflex_dependency_spec(line) for line in requirements_text.splitlines() |
| 78 | + ) |
| 79 | + |
| 80 | + |
| 81 | +def _is_reflex_dependency_spec(requirement: str) -> bool: |
| 82 | + """Check whether a dependency specification refers to the reflex package. |
| 83 | +
|
| 84 | + Args: |
| 85 | + requirement: The dependency specification to check. |
| 86 | +
|
| 87 | + Returns: |
| 88 | + Whether the specification refers to the reflex package. |
| 89 | + """ |
| 90 | + requirement = requirement.strip() |
| 91 | + if not requirement.lower().startswith("reflex"): |
| 92 | + return False |
| 93 | + |
| 94 | + suffix = requirement[len("reflex") :] |
| 95 | + if suffix.startswith("["): |
| 96 | + extras_end = suffix.find("]") |
| 97 | + if extras_end == -1: |
| 98 | + return False |
| 99 | + suffix = suffix[extras_end + 1 :] |
| 100 | + |
| 101 | + return not suffix or suffix.lstrip().startswith(( |
| 102 | + "==", |
| 103 | + "!=", |
| 104 | + ">=", |
| 105 | + "<=", |
| 106 | + "~=", |
| 107 | + ">", |
| 108 | + "<", |
| 109 | + ";", |
| 110 | + "@", |
| 111 | + )) |
| 112 | + |
| 113 | + |
| 114 | +def initialize_requirements_txt( |
| 115 | + requirements_file_path: Path = Path(constants.RequirementsTxt.FILE), |
| 116 | + pyproject_file_path: Path = Path(constants.PyprojectToml.FILE), |
| 117 | +) -> bool: |
46 | 118 | """Initialize the requirements.txt file. |
47 | | - If absent and no pyproject.toml file exists, generate one for the user. |
48 | | - If the requirements.txt does not have reflex as dependency, |
49 | | - generate a requirement pinning current version and append to |
50 | | - the requirements.txt file. |
| 119 | +
|
| 120 | + If a project already uses pyproject.toml, leave dependency management to the |
| 121 | + package manager. Otherwise ensure requirements.txt pins the current Reflex |
| 122 | + version for legacy workflows. |
51 | 123 |
|
52 | 124 | Returns: |
53 | 125 | True if the user has to update the requirements.txt file. |
54 | | -
|
55 | | - Raises: |
56 | | - SystemExit: If the requirements.txt file cannot be read or written to. |
57 | 126 | """ |
58 | | - requirements_file_path = Path(constants.RequirementsTxt.FILE) |
59 | | - if ( |
60 | | - not requirements_file_path.exists() |
61 | | - and Path(constants.PyprojectToml.FILE).exists() |
62 | | - ): |
63 | | - return True |
| 127 | + if not requirements_file_path.exists() and pyproject_file_path.exists(): |
| 128 | + return False |
64 | 129 |
|
65 | 130 | requirements_file_path.touch(exist_ok=True) |
66 | 131 |
|
67 | | - for encoding in [None, "utf-8"]: |
68 | | - try: |
69 | | - content = requirements_file_path.read_text(encoding) |
70 | | - break |
71 | | - except UnicodeDecodeError: |
72 | | - continue |
73 | | - except Exception as e: |
74 | | - console.error(f"Failed to read {requirements_file_path} due to {e}.") |
75 | | - raise SystemExit(1) from None |
76 | | - else: |
| 132 | + content, encoding = _read_dependency_file(requirements_file_path) |
| 133 | + if content is None: |
77 | 134 | return True |
78 | 135 |
|
79 | | - for line in content.splitlines(): |
80 | | - if re.match(r"^reflex[^a-zA-Z0-9]", line): |
81 | | - console.debug(f"{requirements_file_path} already has reflex as dependency.") |
82 | | - return False |
| 136 | + if _has_reflex_requirement_line(content): |
| 137 | + console.debug(f"{requirements_file_path} already has reflex as dependency.") |
| 138 | + return False |
83 | 139 |
|
84 | 140 | console.debug( |
85 | 141 | f"Appending {constants.RequirementsTxt.DEFAULTS_STUB} to {requirements_file_path}" |
|
0 commit comments