diff --git a/.flake8 b/.flake8 deleted file mode 100644 index da0f1a7..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length = 88 -extend-ignore = E203 -exclude = __pycache__,build,dist,notes diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 9d181f1..6cab2c6 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -2,37 +2,28 @@ name: trading-ig integration test on: - schedule: - - cron: '25 2 * * 1-4' +# schedule: +# - cron: '25 2 * * 1-4' workflow_dispatch: jobs: build: - if: github.repository == 'ig-python/trading-ig' + # if: github.repository == 'ig-python/trading-ig' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.10 - uses: actions/setup-python@v5 + - uses: actions/checkout@v6 + + - name: Install uv and setup Python + uses: astral-sh/setup-uv@v7 with: + version: "0.11.6" python-version: "3.10" - - name: Install Poetry - uses: abatilo/actions-poetry@v4 - with: - poetry-version: "latest" - - name: Setup a local virtual environment - run: | - poetry config virtualenvs.create true --local - poetry config virtualenvs.in-project true --local - - uses: actions/cache@v3 - name: Define a cache for the virtual environment based on the dependencies lock file - with: - path: ./.venv - key: venv-${{ hashFiles('poetry.lock') }} + - name: Install dependencies run: | - poetry install --extras "pandas munch tenacity" + uv sync --all-extras --dev + - name: Integration test with pytest env: IG_SERVICE_USERNAME: ${{ secrets.IG_SERVICE_USERNAME }} @@ -41,10 +32,11 @@ jobs: IG_SERVICE_ACC_TYPE: ${{ secrets.IG_SERVICE_ACC_TYPE }} IG_SERVICE_ACC_NUMBER: ${{ secrets.IG_SERVICE_ACC_NUMBER_1 }} run: | - poetry run coverage run -m --source=trading_ig pytest --log-cli-level=DEBUG --log-format="%(asctime)s %(levelname)s %(message)s" --log-date-format="%Y-%m-%d %H:%M:%S" - poetry run coverage report + uv run coverage run -m --source=trading_ig pytest --log-cli-level=DEBUG --log-format="%(asctime)s %(levelname)s %(message)s" --log-date-format="%Y-%m-%d %H:%M:%S" + uv run coverage report + - name: Coveralls env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - poetry run coveralls --service=github + uv run coveralls --service=github diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index eba0be8..078d817 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -10,19 +10,25 @@ jobs: runs-on: ubuntu-latest + environment: + name: pypi + + permissions: + id-token: write + contents: read + steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 + - name: Checkout + uses: actions/checkout@v6 + + - name: Install uv and Python + uses: astral-sh/setup-uv@v7 with: + version: "0.11.6" python-version: "3.10" - - name: Install Poetry - uses: abatilo/actions-poetry@v3 - with: - poetry-version: "latest" - - name: Build and publish - env: - PYPI_USERNAME: __token__ - PYPI_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: | - poetry publish -u $PYPI_USERNAME -p $PYPI_PASSWORD --build + + - name: Build + run: uv build + + - name: Publish + run: uv publish diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 6ca19bc..48b0d30 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -17,31 +17,26 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Install uv and setup Python + uses: astral-sh/setup-uv@v7 with: + version: "0.11.6" python-version: ${{ matrix.python-version }} - - name: Install Poetry - uses: abatilo/actions-poetry@v3 - with: - poetry-version: "latest" - - name: Install dependencies run: | - poetry install --extras "pandas munch tenacity" + uv sync --all-extras --dev - - name: Prettify with black - uses: psf/black@stable - with: - version: "23.3.0" + - name: Check code prettiness with ruff + run: | + uv run ruff format --check - - name: Lint with flake8 + - name: Lint with ruff run: | - poetry run flake8 trading_ig docs sample tests + uv run ruff check trading_ig docs sample tests - name: Unit tests with pytest run: | - poetry run pytest --ignore=tests/test_integration.py + uv run pytest --ignore=tests/test_integration.py diff --git a/README.rst b/README.rst index a84e655..6fd0c7f 100644 --- a/README.rst +++ b/README.rst @@ -62,33 +62,13 @@ For full details, see `pyproject.toml `_. - -Adding to an existing Poetry project:: - - $ poetry add trading-ig - -With all the optional dependencies:: - - $ poetry add trading-ig[pandas,munch,tenacity] - -Cloning the project with Poetry:: - - $ git clone https://github.com/ig-python/trading-ig - $ cd trading-ig - $ poetry install - -And with all optional dependencies:: - - $ poetry install --extras "pandas munch tenacity" - Installing with pip:: $ pip install trading-ig -And with all optional dependencies:: +Installing with all optional dependencies:: - $ pip install trading-ig pandas munch tenacity + $ pip install "trading-ig[pandas, munch, tenacity]" What if I need help? -------------------- diff --git a/docs/source/faq.rst b/docs/source/faq.rst index 83eb47e..b2486a1 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -77,7 +77,7 @@ exceeded one of the limits. You can query the limits associated with either a LIVE or DEMO API key after logging on by calling:: -IGService.get_client_apps() + IGService.get_client_apps() This is the rate used when a new IGService object is created with the use_rate_limiter kwarg set as True. @@ -212,33 +212,33 @@ solutions: How do I check my PR will pass CI checks? ----------------------------------------- This project uses some automated continuous integration (CI) processes whenever -any code is committed, or if someone creates a PR. There are unit tests, code -formatting with ``black``, and linting with ``flake8``. In addition, an -integration test gets executed every night. The integration test takes a long -time due to the :ref:`rate limits`. Before making a PR, please make -sure the tests pass - PRs will be rejected if they do not. For code formatting:: +any code is committed, or if someone creates a PR. There are unit tests, and code +formatting and linting with ``ruff``. In addition, an integration test gets +executed every night. The integration test takes a long time due to the +:ref:`rate limits`. Before making a PR, please make sure the tests +pass - PRs will be rejected if they do not. For code formatting:: - $ poetry run black . + $ uv run ruff format and for linting:: - $ poetry run flake8 trading_ig docs sample tests + $ uv run ruff check trading_ig docs sample tests for unit tests:: - $ poetry run pytest --ignore=tests/test_integration.py + $ uv run pytest --ignore=tests/test_integration.py for integration tests:: - $ poetry run pytest tests/test_integration.py + $ uv run pytest tests/test_integration.py for unit and integration tests:: - $ poetry run pytest + $ uv run pytest for all tests, including one *really* long running one that tests v3 sessions:: - $ poetry run pytest --runslow + $ uv run pytest --runslow .. _v2_or_v3_sessions: @@ -332,10 +332,12 @@ An issue without all this information may be ignored and/or closed without respo What happened to ``setup.py`` and ``requirements.txt``? ------------------------------------------------------------- -Early versions of this project used the standard ``setup.py`` config, with a ``requirements.txt`` file describing -dependencies. `Poetry `_ -support was added with version 0.0.10 (July 2021). The old style config was removed with version 0.0.14 +Early versions of this project used the standard ``setup.py`` config, with a +``requirements.txt`` file describing dependencies. `Poetry `_ +support was added with version 0.0.10 (July 2021).The old style config was +removed with version 0.0.14. +We switched to `uv `_ in April 2026. .. _why-is-pandas-an-optional-dependency-in-pyproject-toml: .. _optional-dependencies: diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index cb86312..b5f456a 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -4,34 +4,13 @@ Quickstart Installation ------------ -This project uses `Poetry `_. - -Adding to an existing Poetry project:: - - $ poetry add trading-ig - -With all the optional dependencies:: - - $ poetry add trading-ig[pandas,munch,tenacity] - -Cloning the project with Poetry:: - - $ git clone https://github.com/ig-python/trading-ig - $ cd trading-ig - $ poetry install - -And with all optional dependencies:: - - $ poetry install --extras "pandas munch tenacity" - Installing with pip:: $ pip install trading-ig -And with all optional dependencies:: - - $ pip install trading-ig pandas munch tenacity +Installing with all optional dependencies:: + $ pip install "trading-ig[pandas, munch, tenacity]" Configuration ------------- diff --git a/pyproject.toml b/pyproject.toml index 517ef25..f7b15a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,26 @@ -[tool.poetry] +[build-system] +requires = ["uv_build>=0.11.6,<0.12.0"] +build-backend = "uv_build" + +[project] name = "trading-ig" version = "0.0.23" description = "A lightweight Python wrapper for the IG Markets API" -authors = ["Femto Trader ", "Andy Geach "] -maintainers = ["Andy Geach "] -license = "BSD-3-Clause" +authors = [ + { name = "Femto Trader", email = "femto.trader@gmail.com" }, + { name = "Andy Geach", email = "andy@bugorfeature.net" }, +] +requires-python = ">=3.10,<4" readme = "README.rst" -homepage = "https://github.com/ig-python/trading-ig" -repository = "https://github.com/ig-python/trading-ig" -documentation = "https://trading-ig.readthedocs.io/" -keywords = ["trading", "spread betting", "CFDs"] +license = "BSD-3-Clause" +maintainers = [ + { name = "Andy Geach", email = "andy@bugorfeature.net" } +] +keywords = [ + "trading", + "spread betting", + "CFDs", +] classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", @@ -18,47 +29,44 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Cython", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", "Topic :: Software Development :: Libraries", - "License :: OSI Approved :: BSD License", +] +dependencies = [ + "requests>=2.24,<3", + "pycryptodome>=3.9,<4", + "requests-cache>=0.5,<0.6", + "six>=1.15,<2", + "lightstreamer-client-lib==1.0.3", ] -[tool.poetry.dependencies] -python = "^3.9" -requests = "^2.24" -pycryptodome = "^3.9" -requests-cache = "^0.5" -six = "^1.15" -lightstreamer-client-lib = "^1.0.3" +[project.optional-dependencies] +pandas = ["pandas>=2,<3"] +munch = ["munch>=2.5,<3"] +tenacity = ["tenacity>=8,<9"] -pandas = {version = "^2", optional = true} -munch = {version = "^2.5", optional = true} -tenacity = {version = "^8", optional = true} +[project.urls] +Homepage = "https://github.com/ig-python/trading-ig" +Repository = "https://github.com/ig-python/trading-ig" +Documentation = "https://trading-ig.readthedocs.io/" +Issues = "https://github.com/ig-python/trading-ig/issues" -[tool.poetry.extras] -pandas = ["pandas"] -munch = ["munch"] -tenacity = ["tenacity"] -sphinx = ["sphinx"] -docs = [ - "sphinx" +[dependency-groups] +dev = [ + "ruff", + "pytest>=8,<9", + "responses>=0.25,<0.26", + "coveralls>=3,<4", + "sphinx", + "sphinx-rtd-theme", ] -[tool.poetry.group.dev.dependencies] -flake8 = "^7" -pytest = "^8" -responses = "^0.25" -coveralls = "^3" -sphinx-rtd-theme = {version = "^1.0.0", optional = true} -black = "23.3.0" - -[tool.poetry.urls] -"Issues" = "https://github.com/ig-python/trading-ig/issues" - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.uv.build-backend] +module-name = "trading_ig" +module-root = "" diff --git a/sample/rest.ipynb b/sample/rest.ipynb index 6783168..f9f06c2 100644 --- a/sample/rest.ipynb +++ b/sample/rest.ipynb @@ -33,7 +33,7 @@ "service.create_session()\n", "\n", "# creating a v3 session\n", - "#service.create_session(version=\"3\")" + "# service.create_session(version=\"3\")" ] }, { @@ -78,7 +78,7 @@ " resolution=\"D\",\n", " start_date=\"2024-01-01\",\n", " end_date=\"2024-01-22\",\n", - " format=service.flat_prices\n", + " format=service.flat_prices,\n", ")\n", "\n", "data[\"prices\"]" @@ -103,10 +103,10 @@ "source": [ "from pathlib import Path\n", "\n", - "save_path = Path('~').expanduser() / \"prices.csv\"\n", + "save_path = Path(\"~\").expanduser() / \"prices.csv\"\n", "print(f\"saving historic prices to {save_path}\")\n", "\n", - "data[\"prices\"].to_csv(save_path, date_format=\"%Y-%m-%dT%H:%M:%S%z\")\n" + "data[\"prices\"].to_csv(save_path, date_format=\"%Y-%m-%dT%H:%M:%S%z\")" ] }, { @@ -127,6 +127,7 @@ "outputs": [], "source": [ "from datetime import datetime, timedelta\n", + "\n", "to_date = datetime.now()\n", "from_date = to_date - timedelta(days=7)\n", "\n", diff --git a/tests/test_activities.py b/tests/test_activities.py index 88f564f..5a3e1d6 100644 --- a/tests/test_activities.py +++ b/tests/test_activities.py @@ -7,7 +7,6 @@ class TestActivities: - """ unit tests for activities """ diff --git a/tests/test_historical_prices.py b/tests/test_historical_prices.py index 8d098e9..0d139e8 100644 --- a/tests/test_historical_prices.py +++ b/tests/test_historical_prices.py @@ -125,9 +125,7 @@ def test_historical_prices_v3_num_points_bad_numpoints(self): responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, - json={ - "errorCode": "Unable to convert value=3.14159 to type= Integer int" - }, # noqa + json={"errorCode": "Unable to convert value=3.14159 to type= Integer int"}, # noqa status=400, ) @@ -420,9 +418,7 @@ def test_historical_prices_by_epic_and_num_points_bad_numpoints(self): responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, - json={ - "errorCode": "Unable to convert value=3.14159 to type= Integer int" - }, # noqa + json={"errorCode": "Unable to convert value=3.14159 to type= Integer int"}, # noqa status=400, ) diff --git a/tests/test_historical_prices_flat.py b/tests/test_historical_prices_flat.py index 029105b..c1a5add 100644 --- a/tests/test_historical_prices_flat.py +++ b/tests/test_historical_prices_flat.py @@ -130,9 +130,7 @@ def test_historical_prices_v3_num_points_bad_numpoints(self): responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, - json={ - "errorCode": "Unable to convert value=3.14159 to type= Integer int" - }, # noqa + json={"errorCode": "Unable to convert value=3.14159 to type= Integer int"}, # noqa status=400, ) diff --git a/tests/test_positions.py b/tests/test_positions.py index e69a0dc..e746992 100644 --- a/tests/test_positions.py +++ b/tests/test_positions.py @@ -5,7 +5,6 @@ class TestPositions: - """ unit tests for position methods """ diff --git a/trading_ig/lightstreamer.py b/trading_ig/lightstreamer.py index 65e2a97..0d6be9c 100644 --- a/trading_ig/lightstreamer.py +++ b/trading_ig/lightstreamer.py @@ -191,8 +191,7 @@ def connect(self): if not notify and sys.platform.startswith("linux"): log.warning( - "systemd.daemon not available, " - "no watchdog notifications will be sent." + "systemd.daemon not available, no watchdog notifications will be sent." ) self._stream_connection = self._call( diff --git a/trading_ig/rest.py b/trading_ig/rest.py index aaf1236..692c3e4 100644 --- a/trading_ig/rest.py +++ b/trading_ig/rest.py @@ -359,10 +359,9 @@ def _get_session(self, session): if session is None: session = self.session # requests Session else: - assert isinstance( - session, Session - ), "session must be not %s" % type( - session + assert isinstance(session, Session), ( + "session must be not %s" + % type(session) ) session = session return session