diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md new file mode 100644 index 00000000..a28e413e --- /dev/null +++ b/.cursor/BUGBOT.md @@ -0,0 +1,10 @@ +# Bugbot Review Rules + +## Core Principle: Comprehensive First-Pass Reviews + +**CRITICAL INSTRUCTION**: You must perform a complete, exhaustive analysis on the FIRST review of any changeset. Do NOT hold back observations or defer issues to later reviews. All feedback must be provided upfront. + +## Expected Behavior + +Your first review should be comprehensive enough that subsequent reviews only need to address newly changed code. The goal is to eliminate "surprise" feedback on code that was part of the initial changeset but somehow escaped earlier review. + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0e28f6a9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + + - package-ecosystem: pip + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..f27988c3 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +name: Build + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + package: + name: Build distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: pyproject.toml + - name: Install build tools + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[dev]" + - name: Build and verify package + run: | + python -m build + python -m twine check dist/* diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c2a97a51..75fe04e5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,9 +1,19 @@ name: Lint -on: [push] +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read jobs: lint: + name: Lint + runs-on: ubuntu-latest steps: @@ -11,9 +21,13 @@ jobs: - uses: actions/setup-python@v6 with: - python-version: "3.10" + python-version: "3.14" cache: 'pip' - cache-dependency-path: setup.py + cache-dependency-path: pyproject.toml - - run: pip3 install -e '.[test]' - - run: make lint-ci + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[dev]" + - name: Run lint + run: make lint-ci diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index adb4a249..a626f658 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,15 +1,25 @@ name: Test -on: [push] +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read jobs: test: + name: Test Python ${{ matrix.python-version }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] + python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] steps: - uses: actions/checkout@v6 @@ -18,7 +28,11 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' - cache-dependency-path: setup.py + cache-dependency-path: pyproject.toml - - run: pip3 install -e '.[test]' - - run: make test + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e . + - name: Run tests + run: make test diff --git a/Makefile b/Makefile index 0d6122af..d800d4b8 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,28 @@ +PYTHON ?= python3 + install: - pip install --edit .[test] + $(PYTHON) -m pip install -e ".[dev]" + +build: + rm -rf build + $(PYTHON) -m build test: - python -m unittest customerio/analytics/test/*.py -v + $(PYTHON) -m unittest customerio/analytics/test/*.py -v lint: - pylint --rcfile=.pylintrc --reports=y --exit-zero customerio/analytics - flake8 --max-complexity=10 --statistics --exit-zero customerio/analytics + $(PYTHON) -m pylint --rcfile=.pylintrc --reports=y --exit-zero customerio/analytics + $(PYTHON) -m flake8 --max-complexity=10 --statistics --exit-zero customerio/analytics lint-ci: - pylint --rcfile=.pylintrc --exit-zero --fail-on=E customerio/analytics - flake8 --max-complexity=10 --max-line-length=100 --statistics customerio/analytics + $(PYTHON) -m pylint --rcfile=.pylintrc --exit-zero --fail-on=E customerio/analytics + $(PYTHON) -m flake8 --max-complexity=10 --max-line-length=100 --statistics customerio/analytics clean: + rm -rf MANIFEST build dist customerio.egg-info + +clean-venv: rm -rf .venv mise deps -.PHONY: install test lint lint-ci clean +.PHONY: install build test lint lint-ci clean clean-venv diff --git a/README.md b/README.md index 7c2f61b4..c82790fe 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ analytics.track(user_id=4, event='order_complete') ## Other Regions -If you're using a [different data center](https://customer.io/docs/accounts-and-workspaces/data-centers/) such as our EU region, you can specify an alternate endpoint: +If you're using a [different data center](https://docs.customer.io/accounts/settings/data-centers/) such as our EU region, you can specify an alternate endpoint: ```python from customerio import analytics diff --git a/customerio/analytics/test/client.py b/customerio/analytics/test/client.py index 512e136e..5fc58350 100644 --- a/customerio/analytics/test/client.py +++ b/customerio/analytics/test/client.py @@ -1,7 +1,7 @@ from datetime import date, datetime import unittest +import unittest.mock as mock import time -import mock from customerio.analytics.version import VERSION from customerio.analytics.client import Client diff --git a/customerio/analytics/test/consumer.py b/customerio/analytics/test/consumer.py index 057f6338..d10ded2e 100644 --- a/customerio/analytics/test/consumer.py +++ b/customerio/analytics/test/consumer.py @@ -1,8 +1,7 @@ import json import time import unittest - -import mock +import unittest.mock as mock try: from queue import Queue diff --git a/customerio/analytics/version.py b/customerio/analytics/version.py index 1e5a6058..926cb3d0 100644 --- a/customerio/analytics/version.py +++ b/customerio/analytics/version.py @@ -1 +1,3 @@ -VERSION = '1.0.0' +from importlib.metadata import version + +VERSION = version("customerio_cdp_analytics") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6025c610 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,56 @@ +[build-system] +requires = ["setuptools>=77", "setuptools-scm>=8", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "customerio_cdp_analytics" +dynamic = ["version"] +# Version is derived from git tags via setuptools-scm (e.g. tag v3.0.0 → version 3.0.0) +description = "Customer.io Data Pipelines (CDP) Python bindings." +readme = "README.md" +requires-python = ">=3.9.0" +license = "MIT" +license-files = ["LICENSE"] +authors = [ + { name = "Peaberry Software Inc.", email = "support@customerio.com" }, +] +dependencies = [ + "requests>=2.32.4", + "monotonic~=1.6", + "backoff~=2.2", + "python-dateutil~=2.8", +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "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", +] + +[project.optional-dependencies] +dev = [ + "build>=1.2.2", + "twine>=6.1.0", + "pylint>=3.2.0", + "flake8>=7.1.0", +] + +[project.urls] +Homepage = "https://github.com/customerio/cdp-analytics-python" +Releases = "https://github.com/customerio/cdp-analytics-python/releases" +Issues = "https://github.com/customerio/cdp-analytics-python/issues" + +[tool.setuptools_scm] +version_scheme = "guess-next-dev" +local_scheme = "no-local-version" + +[tool.setuptools.packages.find] +include = ["customerio*"] diff --git a/requirements.txt b/requirements.txt index 86364777..6a9d363f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ backoff==2.2.1 flake8==3.7.9 monotonic==1.6 -mock==2.0.0 pylint==3.3.3 python-dateutil==2.8.2 requests>=2.32.4 \ No newline at end of file diff --git a/setup.py b/setup.py index e2463395..df789d21 100644 --- a/setup.py +++ b/setup.py @@ -1,63 +1,3 @@ -import os -import sys +from setuptools import find_packages, setup -try: - from setuptools import setup -except ImportError: - from distutils.core import setup -# Don't import the module here, since deps may not be installed -sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'customerio','analytics')) -from version import VERSION - -long_description = ''' -Customer.io Data Pipelines (CDP) is a customer data platform to improve decision-making with real-time updates and deliver personalized experiences. - -This is the official python client that wraps the Customer.io Data Pipelines REST API (https://customer.io/docs/cdp/getting-started/cdp-getting-started/). -''' - -install_requires = [ - "requests>=2.32.4", - "monotonic~=1.6", - "backoff~=2.2", - "python-dateutil~=2.8" -] - -tests_require = [ - "mock==2.0.0", - "pylint>=3.2.0", - "flake8==3.7.9", -] - -setup( - name='customerio_cdp_analytics', - version=VERSION, - url='https://github.com/customerio/cdp-analytics-python', - author='Customer.io', - author_email='cdp@customer.io', - maintainer='Customer.io', - maintainer_email='cdp@customer.io', - test_suite='analytics.test.all', - packages=['customerio.analytics'], - python_requires='>=3.8.0', - license='MIT License', - install_requires=install_requires, - extras_require={ - 'test': tests_require - }, - description='Customer.io Data Pipelines (CDP) is a customer data platform to improve decision-making with real-time updates and deliver personalized experiences.', - long_description=long_description, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "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", - ], -) +setup(packages=find_packages(include=["customerio", "customerio.*"]))