pybinaryguard 1.0.0__tar.gz → 1.0.1__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 (110) hide show
  1. {pybinaryguard-1.0.0/src/pybinaryguard.egg-info → pybinaryguard-1.0.1}/PKG-INFO +1 -1
  2. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/pyproject.toml +1 -1
  3. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/__init__.py +1 -1
  4. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/python_probe.py +25 -16
  5. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1/src/pybinaryguard.egg-info}/PKG-INFO +1 -1
  6. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_diagnostics.py +1 -1
  7. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_rules.py +7 -7
  8. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_scanner.py +1 -1
  9. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/LICENSE +0 -0
  10. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/README.md +0 -0
  11. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/setup.cfg +0 -0
  12. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/__main__.py +0 -0
  13. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/_compat/__init__.py +0 -0
  14. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/__init__.py +0 -0
  15. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/guard.py +0 -0
  16. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/recommender.py +0 -0
  17. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/schema.py +0 -0
  18. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/simulator.py +0 -0
  19. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/agent/tool_interface.py +0 -0
  20. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/__init__.py +0 -0
  21. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/base.py +0 -0
  22. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/dependency_analyzer.py +0 -0
  23. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/elf_analyzer.py +0 -0
  24. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/symbol_analyzer.py +0 -0
  25. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/analyzers/wheel_analyzer.py +0 -0
  26. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/cli/__init__.py +0 -0
  27. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/cli/commands.py +0 -0
  28. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/cli/formatters.py +0 -0
  29. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/cli/main.py +0 -0
  30. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/diagnostics/__init__.py +0 -0
  31. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/diagnostics/explainer.py +0 -0
  32. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/diagnostics/findings.py +0 -0
  33. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/diagnostics/suggestions.py +0 -0
  34. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/frameworks/__init__.py +0 -0
  35. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/frameworks/onnxruntime.py +0 -0
  36. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/frameworks/pytorch.py +0 -0
  37. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/frameworks/tensorflow.py +0 -0
  38. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/frameworks/tensorrt.py +0 -0
  39. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/models/__init__.py +0 -0
  40. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/models/enums.py +0 -0
  41. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/models/finding.py +0 -0
  42. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/models/package.py +0 -0
  43. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/models/system.py +0 -0
  44. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/__init__.py +0 -0
  45. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/contrib/__init__.py +0 -0
  46. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/contrib/gstreamer.py +0 -0
  47. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/contrib/jetson.py +0 -0
  48. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/contrib/opencv.py +0 -0
  49. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/contrib/tensorrt.py +0 -0
  50. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/hooks.py +0 -0
  51. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/plugins/loader.py +0 -0
  52. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/predictor/__init__.py +0 -0
  53. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/predictor/dependency_graph.py +0 -0
  54. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/predictor/linker_simulator.py +0 -0
  55. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/predictor/predictor.py +0 -0
  56. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/predictor/resolver.py +0 -0
  57. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/__init__.py +0 -0
  58. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/base.py +0 -0
  59. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/board_probe.py +0 -0
  60. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/cpu_probe.py +0 -0
  61. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/glibc_probe.py +0 -0
  62. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/gpu_probe.py +0 -0
  63. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/library_probe.py +0 -0
  64. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/os_probe.py +0 -0
  65. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/toolchain_probe.py +0 -0
  66. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/probes/venv_probe.py +0 -0
  67. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/profiles/__init__.py +0 -0
  68. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/profiles/engine.py +0 -0
  69. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/__init__.py +0 -0
  70. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/base.py +0 -0
  71. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/__init__.py +0 -0
  72. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/arch_rules.py +0 -0
  73. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/board_profile_rules.py +0 -0
  74. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/container_rules.py +0 -0
  75. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/cpu_rules.py +0 -0
  76. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/cuda_rules.py +0 -0
  77. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/dependency_rules.py +0 -0
  78. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/framework_rules.py +0 -0
  79. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/glibc_rules.py +0 -0
  80. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/numpy_rules.py +0 -0
  81. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/predictive_rules.py +0 -0
  82. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/python_abi_rules.py +0 -0
  83. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/source_build_rules.py +0 -0
  84. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/builtin/venv_rules.py +0 -0
  85. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/rules/engine.py +0 -0
  86. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/scanner.py +0 -0
  87. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/scoring/__init__.py +0 -0
  88. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/scoring/engine.py +0 -0
  89. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/snapshot/__init__.py +0 -0
  90. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/snapshot/generator.py +0 -0
  91. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/snapshot/lockfile.py +0 -0
  92. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/snapshot/verifier.py +0 -0
  93. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/validators/__init__.py +0 -0
  94. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard/validators/import_validator.py +0 -0
  95. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard.egg-info/SOURCES.txt +0 -0
  96. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard.egg-info/dependency_links.txt +0 -0
  97. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard.egg-info/entry_points.txt +0 -0
  98. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard.egg-info/requires.txt +0 -0
  99. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/src/pybinaryguard.egg-info/top_level.txt +0 -0
  100. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_agent_sdk.py +0 -0
  101. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_cli.py +0 -0
  102. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_elf_analyzer.py +0 -0
  103. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_gap_features.py +0 -0
  104. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_models.py +0 -0
  105. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_plugins.py +0 -0
  106. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_probes.py +0 -0
  107. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_rule_engine.py +0 -0
  108. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_scan_modes.py +0 -0
  109. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_scoring.py +0 -0
  110. {pybinaryguard-1.0.0 → pybinaryguard-1.0.1}/tests/test_wheel_analyzer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinaryguard
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Binary Compatibility Intelligence for Python — detect incompatibilities before they crash your program
5
5
  Author-email: S P Pothihai Selvan <po@nuvai.dev>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pybinaryguard"
7
- version = "1.0.0"
7
+ version = "1.0.1"
8
8
  description = "Binary Compatibility Intelligence for Python — detect incompatibilities before they crash your program"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -15,7 +15,7 @@ Quick start::
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
- __version__ = "1.0.0"
18
+ __version__ = "1.0.1"
19
19
 
20
20
  from pybinaryguard.models.enums import ScanMode, Severity
21
21
  from pybinaryguard.models.finding import Finding, ScanReport
@@ -14,7 +14,9 @@ class PythonProbe(ProbeBase):
14
14
 
15
15
  Gathered fields:
16
16
  - ``python_version``: 3-tuple of (major, minor, micro)
17
- - ``python_abi_tag``: e.g. ``"cp312-cp312-linux_x86_64"``
17
+ - ``python_abi_tag``: PEP 425 wheel-style tag, e.g. ``"cp312"``, ``"cp312d"``
18
+ (debug), ``"pypy39_pp73"``. This is the value compared against a wheel's
19
+ ABI tag from its ``WHEEL`` metadata — *not* the raw ``SOABI``.
18
20
  - ``python_implementation``: e.g. ``"cpython"``, ``"pypy"``
19
21
  - ``python_executable``: absolute path to the interpreter
20
22
  - ``stable_abi_supported``: whether the stable ABI (abi3) is supported
@@ -50,27 +52,34 @@ class PythonProbe(ProbeBase):
50
52
 
51
53
  @staticmethod
52
54
  def _get_abi_tag() -> str:
53
- """Build the ABI tag from sysconfig variables.
55
+ """Return the PEP 425 wheel-style ABI tag.
54
56
 
55
- Falls back to a constructed tag from ``sys.implementation`` when
56
- sysconfig does not provide ``SOABI``.
57
+ Examples: ``"cp312"``, ``"cp312d"`` (debug), ``"pypy39_pp73"``.
58
+
59
+ This intentionally does NOT return ``sysconfig.SOABI``
60
+ (``"cpython-312-x86_64-linux-gnu"``) — that string lives in a different
61
+ namespace than the ABI tag inside a wheel's ``WHEEL`` metadata, and
62
+ comparing them directly produces false-positive mismatches on every
63
+ real wheel.
57
64
  """
58
65
  try:
59
- soabi = sysconfig.get_config_var("SOABI")
60
- if soabi:
61
- return str(soabi)
66
+ impl = sys.implementation.name
67
+ major = sys.version_info.major
68
+ minor = sys.version_info.minor
69
+ abiflags = sysconfig.get_config_var("abiflags") or ""
62
70
 
63
- # Fallback: build from implementation info
64
- impl = sys.implementation.name[0:2] # "cp" for cpython
65
- ver = f"{sys.version_info.major}{sys.version_info.minor}"
66
- tag = f"{impl}{ver}"
71
+ if impl == "cpython":
72
+ return f"cp{major}{minor}{abiflags}"
67
73
 
68
- # Append debug/unicode suffixes for older Python builds
69
- abiflags = sysconfig.get_config_var("abiflags") or ""
70
- if abiflags:
71
- tag += abiflags
74
+ if impl == "pypy":
75
+ pypy_ver = getattr(sys, "pypy_version_info", None)
76
+ if pypy_ver is not None:
77
+ return f"pypy{major}{minor}_pp{pypy_ver[0]}{pypy_ver[1]}"
78
+ return f"pp{major}{minor}"
72
79
 
73
- return tag
80
+ # Other implementations (graalpy, ironpython, jython, ...)
81
+ prefix = impl[:2] if impl else "xx"
82
+ return f"{prefix}{major}{minor}{abiflags}"
74
83
  except Exception:
75
84
  return ""
76
85
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinaryguard
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Binary Compatibility Intelligence for Python — detect incompatibilities before they crash your program
5
5
  Author-email: S P Pothihai Selvan <po@nuvai.dev>
6
6
  License-Expression: MIT
@@ -48,7 +48,7 @@ def _finding(
48
48
  def _make_profile(**kwargs: object) -> SystemProfile:
49
49
  defaults = dict(
50
50
  python_version=(3, 12, 0),
51
- python_abi_tag="cpython-312-x86_64-linux-gnu",
51
+ python_abi_tag="cp312",
52
52
  python_implementation="cpython",
53
53
  python_executable="/usr/bin/python3",
54
54
  os_name="Ubuntu",
@@ -42,7 +42,7 @@ def _make_profile(**kwargs: object) -> SystemProfile:
42
42
  """Create a SystemProfile with defaults that can be overridden."""
43
43
  defaults = dict(
44
44
  python_version=(3, 12, 0),
45
- python_abi_tag="cpython-312-x86_64-linux-gnu",
45
+ python_abi_tag="cp312",
46
46
  python_implementation="cpython",
47
47
  python_executable="/usr/bin/python3",
48
48
  stable_abi_supported=True,
@@ -211,7 +211,7 @@ class TestManylinuxTagViolationRule:
211
211
  class TestPythonABIMismatchRule:
212
212
  def test_finding_when_abi_mismatch(self) -> None:
213
213
  rule = PythonABIMismatchRule()
214
- profile = _make_profile(python_abi_tag="cpython-312-x86_64-linux-gnu")
214
+ profile = _make_profile(python_abi_tag="cp312")
215
215
  pkg = _make_package(
216
216
  package_name="scipy",
217
217
  wheel_tags=[WheelTag("cp310", "cp310", "manylinux_2_17_x86_64")],
@@ -223,16 +223,16 @@ class TestPythonABIMismatchRule:
223
223
 
224
224
  def test_no_finding_when_abi_matches(self) -> None:
225
225
  rule = PythonABIMismatchRule()
226
- profile = _make_profile(python_abi_tag="cpython-312-x86_64-linux-gnu")
226
+ profile = _make_profile(python_abi_tag="cp312")
227
227
  pkg = _make_package(
228
- wheel_tags=[WheelTag("cp312", "cpython-312-x86_64-linux-gnu", "manylinux_2_17_x86_64")],
228
+ wheel_tags=[WheelTag("cp312", "cp312", "manylinux_2_17_x86_64")],
229
229
  )
230
230
  findings = rule.evaluate(profile, [pkg])
231
231
  assert len(findings) == 0
232
232
 
233
233
  def test_skips_abi3_tags(self) -> None:
234
234
  rule = PythonABIMismatchRule()
235
- profile = _make_profile(python_abi_tag="cpython-312-x86_64-linux-gnu")
235
+ profile = _make_profile(python_abi_tag="cp312")
236
236
  pkg = _make_package(
237
237
  wheel_tags=[WheelTag("cp312", "abi3", "manylinux_2_17_x86_64")],
238
238
  )
@@ -241,7 +241,7 @@ class TestPythonABIMismatchRule:
241
241
 
242
242
  def test_skips_none_abi(self) -> None:
243
243
  rule = PythonABIMismatchRule()
244
- profile = _make_profile(python_abi_tag="cpython-312-x86_64-linux-gnu")
244
+ profile = _make_profile(python_abi_tag="cp312")
245
245
  pkg = _make_package(
246
246
  wheel_tags=[WheelTag("py3", "none", "any")],
247
247
  )
@@ -250,7 +250,7 @@ class TestPythonABIMismatchRule:
250
250
 
251
251
  def test_skips_pure_python(self) -> None:
252
252
  rule = PythonABIMismatchRule()
253
- profile = _make_profile(python_abi_tag="cpython-312-x86_64-linux-gnu")
253
+ profile = _make_profile(python_abi_tag="cp312")
254
254
  pkg = _make_package(is_pure_python=True)
255
255
  findings = rule.evaluate(profile, [pkg])
256
256
  assert len(findings) == 0
@@ -32,7 +32,7 @@ class StubProbe:
32
32
  def collect(self) -> Dict[str, Any]:
33
33
  return {
34
34
  "python_version": (3, 12, 0),
35
- "python_abi_tag": "cpython-312-x86_64-linux-gnu",
35
+ "python_abi_tag": "cp312",
36
36
  "python_implementation": "cpython",
37
37
  "python_executable": "/usr/bin/python3",
38
38
  "architecture": Architecture.X86_64,
File without changes
File without changes
File without changes