Repository: conda-forge/conda-smithy

Issue:
Linting for v1 recipes fail cuz of false positive (CFEP 25)
See https://github.com/conda-forge/plip-feedstock/pull/12#issuecomment-2535980043 for context.

In short, the below recipe triggers a lint error from the bot (see above PR):

```yaml
requirements:
  host:
    - python ${{ python_min }}
    - pip
    - setuptools
  run:
    - python >=${{ python_min }}
    - lxml >=4.2.1
    - numpy >=1.13.3
    - openbabel >=3.0.0
  run_constraints:
    - ImageMagick >=7.0
    - pymol-open-source >=2.3.0

tests:
  - python:
      imports:
        - plip
      python_version: ${{ python_min }}
      pip_check: false # it fails at detecting openbabel. see https://github.com/conda-forge/openbabel-feedstock/issues/49
  - script:
      - plip --help
    requirements:
      run:
        - python ${{ python_min }}
```

ping @beckermr 

Discussion:
beckermr: @conda-forge/core this one is ready for review!
wolfv: Could also make sense to replace all with `{{` -> `\${{`?
beckermr: not sure.

Patch:
diff --git a/conda_smithy/linter/hints.py b/conda_smithy/linter/hints.py
index bffe3c4f..1397ca1f 100644
--- a/conda_smithy/linter/hints.py
+++ b/conda_smithy/linter/hints.py
@@ -274,7 +274,7 @@ def hint_noarch_python_use_python_min(
         ]:
             if recipe_version == 1:
                 syntax = syntax.replace(
-                    "{{ python_min }}", "${{ python_min }}"
+                    "{{ python_min }}", r"\${{ python_min }}"
                 )
                 report_syntax = report_syntax.replace(
                     "{{ python_min }}", "${{ python_min }}"
diff --git a/news/2184-noarch-py-v1-regex.rst b/news/2184-noarch-py-v1-regex.rst
new file mode 100644
index 00000000..a228a839
--- /dev/null
+++ b/news/2184-noarch-py-v1-regex.rst
@@ -0,0 +1,24 @@
+**Added:**
+
+* <news item>
+
+**Changed:**
+
+* <news item>
+
+**Deprecated:**
+
+* <news item>
+
+**Removed:**
+
+* <news item>
+
+**Fixed:**
+
+* Fixed a bug where ``$`` was not properly escaped in a regex and this caused false-positive hints for
+  v1 recipes and ``noarch: python`` packages. (#2184)
+
+**Security:**
+
+* <news item>


Test patch:
diff --git a/tests/test_lint_recipe.py b/tests/test_lint_recipe.py
index b49f19b3..d1309819 100644
--- a/tests/test_lint_recipe.py
+++ b/tests/test_lint_recipe.py
@@ -3454,6 +3454,36 @@ def test_hint_noarch_python_use_python_min(
             ),
             [],
         ),
+        (
+            """\
+requirements:
+  host:
+    - python ${{ python_min }}
+    - pip
+    - setuptools
+  run:
+    - python >=${{ python_min }}
+    - lxml >=4.2.1
+    - numpy >=1.13.3
+    - openbabel >=3.0.0
+  run_constraints:
+    - ImageMagick >=7.0
+    - pymol-open-source >=2.3.0
+
+tests:
+  - python:
+      imports:
+        - plip
+      python_version: ${{ python_min }}
+      pip_check: false # it fails at detecting openbabel. see https://github.com/conda-forge/openbabel-feedstock/issues/49
+  - script:
+      - plip --help
+    requirements:
+      run:
+        - python ${{ python_min }}
+""",
+            [],
+        ),
     ],
 )
 def test_hint_noarch_python_use_python_min_v1(
@@ -3483,6 +3513,82 @@ def test_hint_noarch_python_use_python_min_v1(
         )
 
 
+def test_hint_noarch_python_from_main_v1():
+    with tempfile.TemporaryDirectory() as tmpdir:
+        with open(os.path.join(tmpdir, "recipe.yaml"), "w") as f:
+            f.write(
+                """\
+context:
+  name: plip
+  version: 2.3.1
+
+package:
+  name: ${{ name|lower }}
+  version: ${{ version }}
+
+source:
+  url: https://pypi.org/packages/source/${{ name[0] }}/${{ name }}/${{ name }}-${{ version }}.tar.gz
+  sha256: 8d62c798b5ef6f3ae6ddd72e87c353bff8263e557dfa5f6ecf699cdf975f04ce
+
+build:
+  number: 1
+  noarch: python
+  script: python -m pip install . --no-build-isolation -vv
+  python:
+    entry_points:
+      - plip = plip.plipcmd:main
+
+requirements:
+  host:
+    - python ${{ python_min }}
+    - pip
+    - setuptools
+  run:
+    - python >=${{ python_min }}
+    - lxml >=4.2.1
+    - numpy >=1.13.3
+    - openbabel >=3.0.0
+  run_constraints:
+    - ImageMagick >=7.0
+    - pymol-open-source >=2.3.0
+
+tests:
+  - python:
+      imports:
+        - plip
+      python_version: ${{ python_min }}
+      pip_check: false # it fails at detecting openbabel. see https://github.com/conda-forge/openbabel-feedstock/issues/49
+  - script:
+      - plip --help
+    requirements:
+      run:
+        - python ${{ python_min }}
+
+about:
+  license: GPL-2.0-only
+  license_file: LICENSE.txt
+  summary: Analyze non-covalent protein-ligand interactions in 3D structures
+  description: |
+    Protein-Ligand Interaction Profiler - Analyze and visualize non-covalent
+    protein-ligand interactions in PDB files according to memo Salentin et al. (2015)
+  homepage: https://github.com/pharmai/plip
+  repository: https://github.com/pharmai/plip
+  documentation: https://github.com/pharmai/plip/blob/master/DOCUMENTATION.md
+
+extra:
+  recipe-maintainers:
+    - hadim
+    - mikemhenry
+"""
+            )
+        lints, hints = linter.main(tmpdir, return_hints=True, conda_forge=True)
+        assert not any(
+            "`noarch: python` recipes should usually follow the syntax in"
+            in hint
+            for hint in hints
+        )
+
+
 def test_lint_recipe_parses_ok():
     with tempfile.TemporaryDirectory() as tmpdir:
         with open(os.path.join(tmpdir, "meta.yaml"), "w") as f: