PyPcre 0.2.10__tar.gz → 0.2.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {pypcre-0.2.10 → pypcre-0.2.12}/PKG-INFO +2 -1
  2. {pypcre-0.2.10 → pypcre-0.2.12}/PyPcre.egg-info/PKG-INFO +2 -1
  3. {pypcre-0.2.10 → pypcre-0.2.12}/README.md +1 -0
  4. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/cache.py +0 -9
  5. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/pcre.py +25 -0
  6. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/pattern_cache.c +0 -22
  7. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/pcre2.c +0 -5
  8. {pypcre-0.2.10 → pypcre-0.2.12}/pyproject.toml +1 -1
  9. {pypcre-0.2.10 → pypcre-0.2.12}/setup_utils.py +92 -22
  10. {pypcre-0.2.10 → pypcre-0.2.12}/LICENSE +0 -0
  11. {pypcre-0.2.10 → pypcre-0.2.12}/MANIFEST.in +0 -0
  12. {pypcre-0.2.10 → pypcre-0.2.12}/PyPcre.egg-info/SOURCES.txt +0 -0
  13. {pypcre-0.2.10 → pypcre-0.2.12}/PyPcre.egg-info/dependency_links.txt +0 -0
  14. {pypcre-0.2.10 → pypcre-0.2.12}/PyPcre.egg-info/top_level.txt +0 -0
  15. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/__init__.py +0 -0
  16. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/error.py +0 -0
  17. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/flags.py +0 -0
  18. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/re_compat.py +0 -0
  19. {pypcre-0.2.10 → pypcre-0.2.12}/pcre/threads.py +0 -0
  20. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/atomic_compat.h +0 -0
  21. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/cache.c +0 -0
  22. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/error.c +0 -0
  23. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/flag.c +0 -0
  24. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/jit_support.c +0 -0
  25. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/memory.c +0 -0
  26. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/pcre2.h +0 -0
  27. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/pcre2_module.h +0 -0
  28. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/string_helpers.c +0 -0
  29. {pypcre-0.2.10 → pypcre-0.2.12}/pcre_ext/util.c +0 -0
  30. {pypcre-0.2.10 → pypcre-0.2.12}/setup.cfg +0 -0
  31. {pypcre-0.2.10 → pypcre-0.2.12}/setup.py +0 -0
  32. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_accuracy.py +0 -0
  33. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_api_parity.py +0 -0
  34. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_basic.py +0 -0
  35. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_bench_string.py +0 -0
  36. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_benchmark.py +0 -0
  37. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_bytes.py +0 -0
  38. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_cache.py +0 -0
  39. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_clobber.py +0 -0
  40. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_clobber_thread.py +0 -0
  41. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_core.py +0 -0
  42. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_errors.py +0 -0
  43. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_flags.py +0 -0
  44. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_jit.py +0 -0
  45. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_memory.py +0 -0
  46. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_module.py +0 -0
  47. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_pattern.py +0 -0
  48. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_setup_utils_multiarch.py +0 -0
  49. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_simd.py +0 -0
  50. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_threaded_backend.py +0 -0
  51. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_transformers_regex.py +0 -0
  52. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_utf8.py +0 -0
  53. {pypcre-0.2.10 → pypcre-0.2.12}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPcre
3
- Version: 0.2.10
3
+ Version: 0.2.12
4
4
  Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -43,6 +43,7 @@ Modern `nogil` Python bindings for the Pcre2 library with `stdlib.re` api compat
43
43
 
44
44
 
45
45
  ## Latest News
46
+ * 03/02/2026 [0.2.11](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.11): Auto-detect `Visual Studio` for `Windows` env during install/compile.
46
47
  * 02/24/2026 [0.2.10](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.10): Allow VisualStudio (VS) compiler version check override via env var.
47
48
  * 12/15/2025 [0.2.8](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.8): Fixed multi-arch Linux os compatibility where both x86_64 and i386 libs of pcre2 are installed.
48
49
  * 10/20/2025 [0.2.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.4): Removed dependency on system having python3-dev packge. python.h will be optimistically downloaded from python.org when needed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPcre
3
- Version: 0.2.10
3
+ Version: 0.2.12
4
4
  Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -43,6 +43,7 @@ Modern `nogil` Python bindings for the Pcre2 library with `stdlib.re` api compat
43
43
 
44
44
 
45
45
  ## Latest News
46
+ * 03/02/2026 [0.2.11](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.11): Auto-detect `Visual Studio` for `Windows` env during install/compile.
46
47
  * 02/24/2026 [0.2.10](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.10): Allow VisualStudio (VS) compiler version check override via env var.
47
48
  * 12/15/2025 [0.2.8](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.8): Fixed multi-arch Linux os compatibility where both x86_64 and i386 libs of pcre2 are installed.
48
49
  * 10/20/2025 [0.2.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.4): Removed dependency on system having python3-dev packge. python.h will be optimistically downloaded from python.org when needed.
@@ -20,6 +20,7 @@ Modern `nogil` Python bindings for the Pcre2 library with `stdlib.re` api compat
20
20
 
21
21
 
22
22
  ## Latest News
23
+ * 03/02/2026 [0.2.11](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.11): Auto-detect `Visual Studio` for `Windows` env during install/compile.
23
24
  * 02/24/2026 [0.2.10](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.10): Allow VisualStudio (VS) compiler version check override via env var.
24
25
  * 12/15/2025 [0.2.8](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.8): Fixed multi-arch Linux os compatibility where both x86_64 and i386 libs of pcre2 are installed.
25
26
  * 10/20/2025 [0.2.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.4): Removed dependency on system having python3-dev packge. python.h will be optimistically downloaded from python.org when needed.
@@ -48,13 +48,6 @@ class _GlobalCacheState:
48
48
  _THREAD_LOCAL = _ThreadCacheState()
49
49
  _GLOBAL_STATE = _GlobalCacheState()
50
50
  _CACHE_STRATEGY = _CacheStrategy.THREAD_LOCAL
51
- _CACHE_STRATEGY_LOCKED = False
52
-
53
-
54
- def _lock_cache_strategy() -> None:
55
- global _CACHE_STRATEGY_LOCKED
56
- if not _CACHE_STRATEGY_LOCKED:
57
- _CACHE_STRATEGY_LOCKED = True
58
51
 
59
52
 
60
53
  def _normalize_strategy(value: str) -> _CacheStrategy:
@@ -178,8 +171,6 @@ def cached_compile(
178
171
  ) -> T:
179
172
  """Compile *pattern* with *flags*, caching wrapper results when hashable."""
180
173
 
181
- _lock_cache_strategy()
182
-
183
174
  if _CACHE_STRATEGY is _CacheStrategy.THREAD_LOCAL:
184
175
  return _cached_compile_thread_local(pattern, flags, wrapper, jit=jit)
185
176
  return _cached_compile_global(pattern, flags, wrapper, jit=jit)
@@ -473,6 +473,31 @@ class Pattern:
473
473
 
474
474
 
475
475
  def compile(pattern: Any, flags: FlagInput = 0) -> Pattern:
476
+ # Fast path for the dominant shape: compile(pattern) with default flags.
477
+ if flags == 0:
478
+ if isinstance(pattern, Pattern):
479
+ return pattern
480
+
481
+ if isinstance(pattern, _CPattern):
482
+ wrapper = Pattern(pattern)
483
+ if get_thread_default():
484
+ wrapper.enable_auto_threads()
485
+ else:
486
+ wrapper.disable_threads()
487
+ return wrapper
488
+
489
+ adjusted_pattern = _apply_regex_compat(pattern, bool(_DEFAULT_COMPAT_REGEX))
490
+ if isinstance(adjusted_pattern, str):
491
+ native_flags = _pcre2.PCRE2_UTF | _pcre2.PCRE2_UCP
492
+ else:
493
+ native_flags = 0
494
+ compiled = cached_compile(adjusted_pattern, native_flags, Pattern, jit=_DEFAULT_JIT)
495
+ if get_thread_default():
496
+ compiled.enable_auto_threads()
497
+ else:
498
+ compiled.disable_threads()
499
+ return compiled
500
+
476
501
  resolved_flags = _normalise_flags(flags)
477
502
  threads_requested = bool(resolved_flags & THREADS)
478
503
  no_threads_requested = bool(resolved_flags & NO_THREADS)
@@ -211,27 +211,6 @@ pattern_cache_teardown(void)
211
211
  atomic_store_explicit(&pattern_cache_global_mode, 0, memory_order_release);
212
212
  }
213
213
 
214
- static void
215
- pattern_cache_touch(PatternCacheState *state, PyObject *cache_key)
216
- {
217
- if (state->order == NULL) {
218
- return;
219
- }
220
- Py_ssize_t idx = PySequence_Index(state->order, cache_key);
221
- if (idx >= 0) {
222
- if (PySequence_DelItem(state->order, idx) < 0) {
223
- PyErr_Clear();
224
- return;
225
- }
226
- } else if (PyErr_Occurred()) {
227
- PyErr_Clear();
228
- return;
229
- }
230
- if (PyList_Append(state->order, cache_key) < 0) {
231
- PyErr_Clear();
232
- }
233
- }
234
-
235
214
  int
236
215
  pattern_cache_lookup(PyObject *cache_key, PatternObject **out_pattern)
237
216
  {
@@ -256,7 +235,6 @@ pattern_cache_lookup(PyObject *cache_key, PatternObject **out_pattern)
256
235
  if (cached != NULL) {
257
236
  Py_INCREF(cached);
258
237
  *out_pattern = (PatternObject *)cached;
259
- pattern_cache_touch(state, cache_key);
260
238
  } else if (PyErr_Occurred()) {
261
239
  pattern_cache_release_state(lock_held);
262
240
  return -1;
@@ -1806,11 +1806,6 @@ Pattern_compile_cached(PyObject *pattern_obj, uint32_t flags, int jit, int jit_e
1806
1806
  return NULL;
1807
1807
  }
1808
1808
 
1809
- if (PyObject_Hash(cache_key) == -1) {
1810
- PyErr_Clear();
1811
- use_cache = 0;
1812
- }
1813
-
1814
1809
  if (use_cache) {
1815
1810
  PatternObject *cached = NULL;
1816
1811
  if (pattern_cache_lookup(cache_key, &cached) == 0) {
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "PyPcre"
12
- version = "0.2.10"
12
+ version = "0.2.12"
13
13
  description = "Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns."
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.9"
@@ -5,6 +5,7 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import json
8
9
  import os
9
10
  import platform
10
11
  import shlex
@@ -20,21 +21,20 @@ import urllib.request
20
21
  from collections.abc import Callable, Iterable
21
22
  from pathlib import Path
22
23
 
23
-
24
24
  from setuptools._distutils.ccompiler import CCompiler, new_compiler
25
25
  from setuptools._distutils.errors import CCompilerError, DistutilsExecError
26
26
  from setuptools._distutils.sysconfig import customize_compiler
27
27
 
28
-
29
28
  _PCRE_EXT_DIR: Path | None = None
30
29
  _PCRE2_REPO_URL: str | None = None
31
30
  _PCRE2_TAG: str | None = None
32
31
 
33
-
34
32
  _LIB_EXTENSIONS: tuple[str, ...] = ()
35
33
  _LIBRARY_BASENAME: str | None = None
36
34
  _LIBRARY_SEARCH_PATTERNS: tuple[str, ...] = ()
37
35
 
36
+ VS_DOWNLOAD_URL = "https://aka.ms/vs"
37
+
38
38
 
39
39
  def _ensure_macos_archflags() -> None:
40
40
  if sys.platform != "darwin":
@@ -51,13 +51,13 @@ def _ensure_macos_archflags() -> None:
51
51
 
52
52
 
53
53
  def configure_environment(
54
- *,
55
- pcre_ext_dir: Path,
56
- repo_url: str,
57
- repo_tag: str,
58
- lib_extensions: Iterable[str],
59
- library_basename: str,
60
- library_search_patterns: Iterable[str],
54
+ *,
55
+ pcre_ext_dir: Path,
56
+ repo_url: str,
57
+ repo_tag: str,
58
+ lib_extensions: Iterable[str],
59
+ library_basename: str,
60
+ library_search_patterns: Iterable[str],
61
61
  ) -> None:
62
62
  """Inject project-specific constants from setup.py."""
63
63
 
@@ -459,6 +459,72 @@ def _clean_previous_build(destination: Path, build_dir: Path, build_roots: list[
459
459
  continue
460
460
 
461
461
 
462
+ def _find_vswhere():
463
+ path = shutil.which("vswhere.exe")
464
+ if path:
465
+ return path
466
+
467
+ candidates = [
468
+ r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe",
469
+ r"%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe",
470
+ r"%ProgramData%\Microsoft\VisualStudio\Installer\vswhere.exe",
471
+ ]
472
+
473
+ for p in candidates:
474
+ full_path = os.path.expandvars(p)
475
+ if os.path.isfile(full_path):
476
+ return full_path
477
+
478
+ return None
479
+
480
+
481
+ def _detect_vs_generator():
482
+ vswhere = _find_vswhere()
483
+ if not vswhere:
484
+ return None
485
+
486
+ result = subprocess.run(
487
+ [
488
+ vswhere,
489
+ "-latest",
490
+ "-products", "*",
491
+ "-requires", "Microsoft.Component.MSBuild",
492
+ "-format", "json"
493
+ ],
494
+ capture_output=True,
495
+ text=True,
496
+ check=False
497
+ )
498
+
499
+ if result.returncode != 0:
500
+ return None
501
+
502
+ data = json.loads(result.stdout or "[]")
503
+ if not data:
504
+ return None
505
+
506
+ info = data[0]
507
+
508
+ ver = info.get("installationVersion", "")
509
+ major = ver.split(".")[0]
510
+
511
+ year = info.get("catalog", {}).get("productLineVersion")
512
+
513
+ if not year:
514
+ year_map = {
515
+ "15": "2017",
516
+ "16": "2019",
517
+ "17": "2022",
518
+ "18": "2026",
519
+ }
520
+ year = year_map.get(major)
521
+
522
+ if year:
523
+ return f"Visual Studio {major} {year}"
524
+
525
+ return f"Visual Studio {major}"
526
+
527
+
462
528
  def _prepare_pcre2_source() -> tuple[list[str], list[str], list[str]]:
463
529
  if (_is_windows_platform() and not _is_wsl_environment()) or _is_solaris_platform():
464
530
  os.environ["PYPCRE_BUILD_FROM_SOURCE"] = "1"
@@ -467,10 +533,10 @@ def _prepare_pcre2_source() -> tuple[list[str], list[str], list[str]]:
467
533
  return ([], [], [])
468
534
 
469
535
  destination = _get_pcre_ext_dir() / _get_repo_tag()
470
- git_dir = destination / ".git"
536
+ sub_git_file = destination / ".git" # on Windows , .git is a file containing "gitdir: <actual_git_dir>"
471
537
  repo_already_present = destination.exists()
472
538
 
473
- if destination.exists() and not git_dir.is_dir():
539
+ if destination.exists() and not sub_git_file.exists():
474
540
  raise RuntimeError(
475
541
  f"Existing directory {destination} is not a git checkout; remove or rename it before building"
476
542
  )
@@ -564,11 +630,17 @@ def _prepare_pcre2_source() -> tuple[list[str], list[str], list[str]]:
564
630
  "-DPCRE2_BUILD_TESTS=OFF",
565
631
  "-DPCRE2_BUILD_PCRE2GREP=OFF",
566
632
  "-DPCRE2_BUILD_PCRE2TEST=OFF",
567
- "-DBUILD_SHARED_LIBS=OFF", # build a static lib
633
+ "-DBUILD_SHARED_LIBS=OFF", # build a static lib
568
634
  ]
569
635
  # On Windows, force MSVC and the /MD runtime. Never let CMake pick MinGW.
570
636
  if _is_windows_platform():
571
- vs_gen = os.environ.get("CMAKE_GENERATOR", "Visual Studio 18 2022")
637
+ vs_gen = os.environ.get("CMAKE_GENERATOR", _detect_vs_generator())
638
+ if not vs_gen:
639
+ raise RuntimeError(
640
+ "Visual Studio not found.\n"
641
+ "Please install Visual Studio Build Tools:\n"
642
+ f"{VS_DOWNLOAD_URL}"
643
+ )
572
644
  cmake_args += [
573
645
  "-G", vs_gen,
574
646
  "-A", "x64",
@@ -769,7 +841,7 @@ def _should_disable_native_flags_for_macos(compiler: CCompiler | None) -> bool:
769
841
 
770
842
 
771
843
  def _compiler_supports_flags(
772
- flags: Iterable[str], *, code: str | None = None
844
+ flags: Iterable[str], *, code: str | None = None
773
845
  ) -> bool:
774
846
  normalized = tuple(str(flag) for flag in flags if flag)
775
847
  source_code = code or "int main(void) { return 0; }\n"
@@ -829,7 +901,7 @@ def _augment_compile_flags(flags: list[str]) -> None:
829
901
  ("-mtune=native", True),
830
902
  ("-fomit-frame-pointer", False),
831
903
  ("-funroll-loops", False),
832
- #("-falign-loops=32", False),
904
+ # ("-falign-loops=32", False),
833
905
  ]
834
906
 
835
907
  seen = set(flags)
@@ -922,7 +994,7 @@ def _macho_class_bits(path: Path, host_bits: int) -> int | None:
922
994
  return None
923
995
  for index in range(nfat_arch):
924
996
  offset = index * arch_entry_size
925
- cputype = struct.unpack(f"{endian}I", arch_data[offset : offset + 4])[0]
997
+ cputype = struct.unpack(f"{endian}I", arch_data[offset: offset + 4])[0]
926
998
  bits = 64 if (cputype & _MACHO_ABI64_FLAG) else 32
927
999
  if bits == host_bits:
928
1000
  return host_bits
@@ -1080,9 +1152,9 @@ def _extend_unique(target: list[str], value: str) -> None:
1080
1152
 
1081
1153
 
1082
1154
  def _extend_with_existing(
1083
- target: list[str],
1084
- candidates: list[Path],
1085
- predicate: Callable[[Path], bool] | None = None,
1155
+ target: list[str],
1156
+ candidates: list[Path],
1157
+ predicate: Callable[[Path], bool] | None = None,
1086
1158
  ) -> None:
1087
1159
  for candidate in candidates:
1088
1160
  if not candidate.is_dir():
@@ -1102,7 +1174,6 @@ def _extend_env_paths(target: list[str], env_var: str) -> None:
1102
1174
  _extend_unique(target, candidate)
1103
1175
 
1104
1176
 
1105
-
1106
1177
  def _python_include_candidates() -> list[Path]:
1107
1178
  candidates: list[Path] = []
1108
1179
  config_paths = sysconfig.get_paths()
@@ -1390,7 +1461,6 @@ is_windows_platform = _is_windows_platform
1390
1461
  is_solaris_platform = _is_solaris_platform
1391
1462
  filter_incompatible_multiarch = _filter_incompatible_multiarch
1392
1463
 
1393
-
1394
1464
  __all__ = [
1395
1465
  "configure_environment",
1396
1466
  "prepare_pcre2_source",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes