Repository: open-feature/python-sdk-contrib (language: python)

Issue:
make test fails
```
$ make test
. venv/bin/activate; pytest tests/
========================================================================================================================================= test session starts ==========================================================================================================================================
platform linux -- Python 3.10.8, pytest-7.4.2, pluggy-1.3.0
rootdir: /workspaces/python-sdk-contrib
plugins: anyio-4.0.0
collected 5 items / 1 error                                                                                                                                                                                                                                                                            

================================================================================================================================================ ERRORS ================================================================================================================================================
______________________________________________________________________________________________________________________________ ERROR collecting tests/hooks/test_otel.py _______________________________________________________________________________________________________________________________
ImportError while importing test module '/workspaces/python-sdk-contrib/tests/hooks/test_otel.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/local/python/3.10.8/lib/python3.10/importlib/__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/hooks/test_otel.py:4: in <module>
    from openfeature.contrib.hooks.otel import TracingHook
openfeature/contrib/hooks/otel.py:5: in <module>
    from opentelemetry import trace
E   ModuleNotFoundError: No module named 'opentelemetry'
======================================================================================================================================= short test summary info ========================================================================================================================================
ERROR tests/hooks/test_otel.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=========================================================================================================================================== 1 error in 0.17s ===========================================================================================================================================
make: *** [Makefile:32: test] Error 2
```



Discussion / hints:
@federicobond, would you be able to take a look at this?
Thanks for reporting @agardnerIT. Yes, it looks for the opentelemetry dependency in the requirements.txt which is not hooked to the makefile.

I believe we have discussed this before, but the current scheme for managing dependencies is needlessly complicated. We can get by with a single requirements.txt with pinned versions which is updated regularly via renovate. I will submit a pull request with a proposal to simplify dependency management.

Fix patch:
diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml
index 64e6376d..35315f7f 100644
--- a/.github/workflows/lint-pr.yml
+++ b/.github/workflows/lint-pr.yml
@@ -19,4 +19,4 @@ jobs:
     steps:
       - uses: amannn/action-semantic-pull-request@v5
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml
index 1e03ad5d..0d72c352 100644
--- a/.github/workflows/merge.yml
+++ b/.github/workflows/merge.yml
@@ -7,14 +7,14 @@ name: Merge
 
 on:
   push:
-    branches: [ master, main ]
+    branches: [master, main]
 
 jobs:
   build:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        container: [ "python:3.8", "python:3.9", "python:3.10" ]
+        container: ["python:3.8", "python:3.9", "python:3.10", "python:3.11"]
     container:
       image: ${{ matrix.container }}
 
@@ -25,15 +25,14 @@ jobs:
         uses: actions/cache@v2
         with:
           path: ~/.venv
-          key: ${{ hashFiles('requirements.txt', 'requirements-dev.txt') }}
+          key: ${{ hashFiles('requirements.txt') }}
 
       - name: Upgrade pip
         run: pip install --upgrade pip
 
       - name: Create and activate Virtualenv
         run: |
-          pip install virtualenv
-          [ ! -d ".venv" ] && virtualenv .venv
+          [ ! -d ".venv" ] && python -m venv .venv
           . .venv/bin/activate
 
       - name: Initialize CodeQL
@@ -42,7 +41,7 @@ jobs:
           languages: python
 
       - name: Install dependencies
-        run: pip install -r requirements-dev.txt
+        run: pip install -r requirements.txt
 
       - name: Run black formatter check
         run: black --check .
@@ -65,4 +64,4 @@ jobs:
           verbose: true # optional (default = false)
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@v2
\ No newline at end of file
+        uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index ee03562d..51ca64ea 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -7,7 +7,7 @@ name: PR
 
 on:
   pull_request:
-    branches: [ master, main ]
+    branches: [master, main]
 
 permissions:
   contents: read
@@ -17,7 +17,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        container: [ "python:3.8", "python:3.9", "python:3.10" ]
+        container: ["python:3.8", "python:3.9", "python:3.10", "python:3.11"]
     container:
       image: ${{ matrix.container }}
 
@@ -28,15 +28,14 @@ jobs:
         uses: actions/cache@v2
         with:
           path: ~/.venv
-          key: ${{ hashFiles('requirements.txt', 'requirements-dev.txt') }}
+          key: ${{ hashFiles('requirements.txt') }}
 
       - name: Upgrade pip
         run: pip install --upgrade pip
 
       - name: Create and activate Virtualenv
         run: |
-          pip install virtualenv
-          [ ! -d ".venv" ] && virtualenv .venv
+          [ ! -d ".venv" ] && python -m venv .venv
           . .venv/bin/activate
 
       - name: Initialize CodeQL
@@ -45,7 +44,7 @@ jobs:
           languages: python
 
       - name: Install dependencies
-        run: pip install -r requirements-dev.txt
+        run: pip install -r requirements.txt
 
       - name: Run black formatter check
         run: black --check .
@@ -68,4 +67,4 @@ jobs:
           verbose: true # optional (default = false)
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@v2
\ No newline at end of file
+        uses: github/codeql-action/analyze@v2
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..62e14512
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,28 @@
+default_stages: [commit]
+repos:
+  - repo: https://github.com/pycqa/isort
+    rev: 5.12.0
+    hooks:
+      - id: isort
+        args: ["--profile", "black", "--filter-files"]
+
+  - repo: https://github.com/psf/black
+    rev: 23.9.1
+    hooks:
+      - id: black
+        language_version: python3.9
+
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.0.1
+    hooks:
+      - id: check-yaml
+      - id: trailing-whitespace
+      - id: check-merge-conflict
+      - id: debug-statements
+
+  - repo: https://github.com/pycqa/flake8
+    rev: 6.1.0
+    hooks:
+      - id: flake8
+        additional_dependencies:
+          ["flake8-print", "flake8-builtins", "flake8-functions==0.0.4"]
diff --git a/Makefile b/Makefile
index 2f3ef056..439daae2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 
-VENV_NAME ?= venv
+VENV_NAME ?= .venv
 VENV_ACTIVATE = . $(VENV_NAME)/bin/activate
 PYTHON = ${VENV_NAME}/bin/python3
 
@@ -8,24 +8,20 @@ PYTHON = ${VENV_NAME}/bin/python3
 .PHONY: help
 help:
 	@echo "Targets:"
-	@echo "  requirements    Compiles requirements.in into requirements.txt"
+	@echo "  requirements    Installs dependencies
 	@echo "  venv            Creates a virtual environment and install dependencies"
 	@echo "  test            Run pytest on the tests/ directory"
 	@echo "  lint            Check code with flake8 and black"
 	@echo "  format          Format code with black"
 
 .PHONY: requirements
-requirements: ## Compiles requirements.in into requirements.txt
-	$(VENV_ACTIVATE); pip install pip-tools
-	$(VENV_ACTIVATE); pip-compile requirements.in
+requirements:
+	$(VENV_ACTIVATE); pip install -r requirements.txt
 
 .PHONY: venv
-venv: $(VENV_NAME)/bin/activate ## Creates a virtual environment and install dependencies
-$(VENV_NAME)/bin/activate: requirements.txt
-	test -d $(VENV_NAME) || virtualenv -p python3 $(VENV_NAME)
-	$(VENV_ACTIVATE); pip install -U pip setuptools
+venv: $(VENV_NAME)
+	python3 -m venv $(VENV_NAME)
 	$(VENV_ACTIVATE); pip install -r requirements.txt
-	touch $(VENV_NAME)/bin/activate
 
 .PHONY: test
 test: venv ## Run pytest on the tests/ directory
@@ -33,9 +29,9 @@ test: venv ## Run pytest on the tests/ directory
 
 .PHONY: lint
 lint: venv ## Check code with flake8 and black
-	$(VENV_ACTIVATE); flake8 src/
-	$(VENV_ACTIVATE); black --check src/
+	$(VENV_ACTIVATE); flake8 .
+	$(VENV_ACTIVATE); black --check .
 
 .PHONY: format
 format: venv ## Format code with black
-	$(VENV_ACTIVATE); black src/
+	$(VENV_ACTIVATE); black .
diff --git a/openfeature/contrib/hooks/otel.py b/openfeature/contrib/hooks/otel.py
index 05562406..083df5dd 100644
--- a/openfeature/contrib/hooks/otel.py
+++ b/openfeature/contrib/hooks/otel.py
@@ -4,7 +4,6 @@
 from openfeature.hook import Hook, HookContext
 from opentelemetry import trace
 
-
 OTEL_EVENT_NAME = "feature_flag"
 
 
diff --git a/openfeature/contrib/providers/README.md b/openfeature/contrib/providers/README.md
index 176a279d..40de8e00 100644
--- a/openfeature/contrib/providers/README.md
+++ b/openfeature/contrib/providers/README.md
@@ -1,5 +1,5 @@
 # OpenFeature Python Providers
-Providers are responsible for performing flag evaluation. They provide an abstraction between the underlying 
-flag management system and OpenFeature itself. This allows providers to be changed without requiring a major 
-code refactor. Please see the [spec]("https://github.com/open-feature/spec/blob/main/specification/sections/02-providers.md") 
+Providers are responsible for performing flag evaluation. They provide an abstraction between the underlying
+flag management system and OpenFeature itself. This allows providers to be changed without requiring a major
+code refactor. Please see the [spec]("https://github.com/open-feature/spec/blob/main/specification/sections/02-providers.md")
 for more details.
diff --git a/openfeature/contrib/providers/flagd/provider.py b/openfeature/contrib/providers/flagd/provider.py
index ff8df3cd..0095f93f 100644
--- a/openfeature/contrib/providers/flagd/provider.py
+++ b/openfeature/contrib/providers/flagd/provider.py
@@ -162,7 +162,6 @@ def __resolve(
         # If lookup worked (200 response) get flag (or empty)
         # This is the "ideal" case.
         if response.status_code == 200:
-
             # Got a valid flag and valid type. Return it.
             if "value" in json_content:
                 # Got a valid flag value for key: {key} of: {json_content['value']}
diff --git a/requirements-dev.in b/requirements-dev.in
deleted file mode 100644
index 891eb891..00000000
--- a/requirements-dev.in
+++ /dev/null
@@ -1,11 +0,0 @@
-pylint
-pytest
-pytest-mock
-black
-pip-tools
-pre-commit
-flake8
-pytest-mock
-coverage
-openfeature-sdk
-opentelemetry-api
diff --git a/requirements-dev.txt b/requirements-dev.txt
deleted file mode 100644
index 76cf7e08..00000000
--- a/requirements-dev.txt
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-#    pip-compile requirements-dev.in
-#
-astroid==2.11.5
-    # via pylint
-attrs==21.4.0
-    # via pytest
-black==22.3.0
-    # via -r requirements-dev.in
-cfgv==3.3.1
-    # via pre-commit
-click==8.1.3
-    # via
-    #   black
-    #   pip-tools
-coverage==7.2.7
-    # via -r requirements-dev.in
-deprecated==1.2.14
-    # via opentelemetry-api
-dill==0.3.4
-    # via pylint
-distlib==0.3.4
-    # via virtualenv
-filelock==3.6.0
-    # via virtualenv
-flake8==4.0.1
-    # via -r requirements-dev.in
-identify==2.5.0
-    # via pre-commit
-importlib-metadata==6.8.0
-    # via opentelemetry-api
-iniconfig==1.1.1
-    # via pytest
-isort==5.10.1
-    # via pylint
-lazy-object-proxy==1.7.1
-    # via astroid
-mccabe==0.6.1
-    # via
-    #   flake8
-    #   pylint
-mypy-extensions==0.4.3
-    # via black
-nodeenv==1.6.0
-    # via pre-commit
-openfeature-sdk==0.3.1
-    # via -r requirements-dev.in
-opentelemetry-api==1.19.0
-    # via -r requirements-dev.in
-packaging==21.3
-    # via pytest
-pathspec==0.9.0
-    # via black
-pep517==0.12.0
-    # via pip-tools
-pip-tools==6.6.0
-    # via -r requirements-dev.in
-platformdirs==2.5.2
-    # via
-    #   black
-    #   pylint
-    #   virtualenv
-pluggy==1.0.0
-    # via pytest
-pre-commit==2.19.0
-    # via -r requirements-dev.in
-py==1.11.0
-    # via pytest
-pycodestyle==2.8.0
-    # via flake8
-pyflakes==2.4.0
-    # via flake8
-pylint==2.13.8
-    # via -r requirements-dev.in
-pyparsing==3.0.9
-    # via packaging
-pytest==7.1.2
-    # via
-    #   -r requirements-dev.in
-    #   pytest-mock
-pytest-mock==3.7.0
-    # via -r requirements-dev.in
-pyyaml==6.0
-    # via pre-commit
-six==1.16.0
-    # via virtualenv
-toml==0.10.2
-    # via pre-commit
-tomli==2.0.1
-    # via
-    #   pep517
-    #   pytest
-virtualenv==20.14.1
-    # via pre-commit
-wheel==0.37.1
-    # via pip-tools
-wrapt==1.14.1
-    # via
-    #   astroid
-    #   deprecated
-zipp==3.16.2
-    # via importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-# pip
-# setuptools
diff --git a/requirements.in b/requirements.in
deleted file mode 100644
index b10cee8b..00000000
--- a/requirements.in
+++ /dev/null
@@ -1,1 +0,0 @@
-openfeature-sdk
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 4e51af4e..5f53c674 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,10 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-#    pip-compile requirements.in
-#
+requests==2.31.0
+opentelemetry-sdk==1.20.0
 openfeature-sdk==0.3.1
-    # via -r requirements.in
+pylint==3.0.0
+pytest==7.4.2
+pytest-mock==3.11.1
+black==23.9.1
+pre-commit==3.4.0
+flake8==6.1.0
+coverage==7.3.2


Test patch:
diff --git a/tests/hooks/test_otel.py b/tests/hooks/test_otel.py
index 9f4080ef..f0bf0ffa 100644
--- a/tests/hooks/test_otel.py
+++ b/tests/hooks/test_otel.py
@@ -1,13 +1,14 @@
 from unittest.mock import Mock
 
 import pytest
-from openfeature.contrib.hooks.otel import TracingHook
 from openfeature.evaluation_context import EvaluationContext
 from openfeature.flag_evaluation import FlagEvaluationDetails, FlagType
 from openfeature.hook import HookContext
 from opentelemetry import trace
 from opentelemetry.trace import Span
 
+from openfeature.contrib.hooks.otel import TracingHook
+
 
 @pytest.fixture
 def mock_get_current_span(monkeypatch):
diff --git a/tests/providers/conftest.py b/tests/providers/conftest.py
index 3b017655..63f5eb96 100644
--- a/tests/providers/conftest.py
+++ b/tests/providers/conftest.py
@@ -1,6 +1,6 @@
 import pytest
-from openfeature import api
 
+from openfeature import api
 from openfeature.contrib.providers.flagd import FlagdProvider
 
 
diff --git a/tests/providers/test_flagd.py b/tests/providers/test_flagd.py
index d6dda5e2..db51719f 100644
--- a/tests/providers/test_flagd.py
+++ b/tests/providers/test_flagd.py
@@ -1,7 +1,6 @@
 from numbers import Number
 
 from openfeature import api
-
 from openfeature.contrib.providers.flagd import FlagdProvider