From 70d330eece5e38d7b930a796c09b6dda9a3a73ea Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 19:52:47 +0300 Subject: [PATCH 01/21] Add mise --- .gitignore | 3 +++ mise.toml | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 mise.toml diff --git a/.gitignore b/.gitignore index 76f2a46..a7b60b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.env.local +.venv + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..f5be97e --- /dev/null +++ b/mise.toml @@ -0,0 +1,5 @@ +[tools] + +[env] +_.file = ".env.local" +_.python.venv = { path = ".venv", create = true } From 7d400bd0ca79ffe53a7b45776feacbf42e33e807 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:09:14 +0300 Subject: [PATCH 02/21] Switch to pytest --- .github/workflows/main.yml | 14 +++++++++----- README.md | 2 +- mise.toml | 6 ++++++ pytest.ini | 12 ++++++++++++ setup.py | 1 + test-requirements.txt | 6 +++--- tests/test_api.py | 27 +++++++++++++-------------- tests/test_detectlanguage.py | 13 ++++++------- tests/utils.py | 4 ---- 9 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 pytest.ini delete mode 100644 tests/utils.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bdbda65..201499b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,12 +8,15 @@ jobs: python-version: - '2.7' - '3.6' - - '3.7' + - '3.8' - '3.9' + - '3.10' + - '3.11' + - '3.12' name: Python ${{ matrix.python-version }} sample steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' @@ -22,6 +25,7 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt pip install -r test-requirements.txt - - env: + - name: Run tests + env: DETECTLANGUAGE_API_KEY: ${{ secrets.DETECTLANGUAGE_API_KEY }} - run: nosetests + run: pytest diff --git a/README.md b/README.md index 43ace25..2b02761 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ You are welcome to patch and send GitHub pull requests. pip install -r requirements.txt pip install -r test-requirements.txt - nosetests + pytest ## License diff --git a/mise.toml b/mise.toml index f5be97e..df9607b 100644 --- a/mise.toml +++ b/mise.toml @@ -3,3 +3,9 @@ [env] _.file = ".env.local" _.python.venv = { path = ".venv", create = true } + +[tasks] +test = "pytest" +test-cov = "pytest --cov=detectlanguage --cov-report=term-missing" +test-verbose = "pytest -v" +install-dev = "pip install -r requirements.txt && pip install -r test-requirements.txt" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..5146207 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,12 @@ +[tool:pytest] +testpaths = tests +python_files = test_*.py +python_classes = Test* +python_functions = test_* +addopts = + --verbose + --tb=short + --strict-markers +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests \ No newline at end of file diff --git a/setup.py b/setup.py index feb7ae1..78a3200 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ download_url = 'https://github.com/detectlanguage/detectlanguage-python', keywords = ['language', 'identification', 'detection', 'api', 'client'], install_requires= ['requests>=2.4.2'], + test_requires= ['pytest>=7.0.0', 'pytest-mock>=3.10.0', 'pytest-cov>=4.0.0'], classifiers = [], license = 'MIT', ) diff --git a/test-requirements.txt b/test-requirements.txt index 8a35f17..d184a00 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,3 @@ -nose -nose-setenv -mock +pytest>=7.0.0 +pytest-mock>=3.10.0 +pytest-cov>=4.0.0 diff --git a/tests/test_api.py b/tests/test_api.py index 0016f57..aacc206 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,34 +1,33 @@ # -*- coding: utf-8 -*- -from . import utils -from nose.tools import * +import pytest import detectlanguage import os -class TestApi(utils.TestCase): - def setUp(self): +class TestApi: + def setup_method(self): detectlanguage.configuration.api_key = os.environ['DETECTLANGUAGE_API_KEY'] def test_simple_detect(self): result = detectlanguage.simple_detect("Hello world") - eq_('en', result) + assert result == 'en' def test_detect(self): result = detectlanguage.detect("Hello world") - eq_('en', result[0]['language']) + assert result[0]['language'] == 'en' def test_detect_unicode(self): result = detectlanguage.detect("Ėjo ežiukas") - eq_('lt', result[0]['language']) + assert result[0]['language'] == 'lt' def test_detect_array(self): result = detectlanguage.detect(["Hello world", "Ėjo ežiukas"]) - eq_('en', result[0][0]['language']) - eq_('lt', result[1][0]['language']) + assert result[0][0]['language'] == 'en' + assert result[1][0]['language'] == 'lt' def test_user_status(self): result = detectlanguage.user_status() - eq_('ACTIVE', result['status']) + assert result['status'] == 'ACTIVE' def test_languages(self): result = detectlanguage.languages() @@ -37,12 +36,12 @@ def test_languages(self): def test_secure(self): detectlanguage.configuration.secure = True result = detectlanguage.detect("Hello world") - eq_('en', result[0]['language']) + assert result[0]['language'] == 'en' detectlanguage.configuration.secure = False -class TestApiErrors(utils.TestCase): - @raises(detectlanguage.DetectLanguageError) +class TestApiErrors: def test_invalid_key(self): detectlanguage.configuration.api_key = 'invalid' - detectlanguage.detect("Hello world") + with pytest.raises(detectlanguage.DetectLanguageError): + detectlanguage.detect("Hello world") \ No newline at end of file diff --git a/tests/test_detectlanguage.py b/tests/test_detectlanguage.py index 4835a1e..6f97109 100644 --- a/tests/test_detectlanguage.py +++ b/tests/test_detectlanguage.py @@ -1,13 +1,12 @@ -from . import utils -from nose.tools import eq_ +import pytest import detectlanguage -class TestDetectlanguage(utils.TestCase): +class TestDetectlanguage: def testDefaults(self): - eq_('0.2', detectlanguage.configuration.api_version) - eq_('ws.detectlanguage.com', detectlanguage.configuration.host) - eq_(False, detectlanguage.configuration.secure) + assert detectlanguage.configuration.api_version == '0.2' + assert detectlanguage.configuration.host == 'ws.detectlanguage.com' + assert detectlanguage.configuration.secure == False def testConfiguration(self): detectlanguage.configuration.api_key = 'TEST' - eq_('TEST', detectlanguage.client.configuration.api_key) + assert detectlanguage.client.configuration.api_key == 'TEST' diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index 9918cd5..0000000 --- a/tests/utils.py +++ /dev/null @@ -1,4 +0,0 @@ -import unittest - -class TestCase(unittest.TestCase): - pass \ No newline at end of file From 35b5ab6516a5e7d0d2c3f3615404b16620bf1af6 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:16:15 +0300 Subject: [PATCH 03/21] Adjust github test action --- .github/workflows/main.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 201499b..bfa6c9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,13 +6,16 @@ jobs: strategy: matrix: python-version: - - '2.7' - '3.6' + - '3.7' - '3.8' - '3.9' - '3.10' - '3.11' - '3.12' + - '3.13' + - '3.14' + - '3.15' name: Python ${{ matrix.python-version }} sample steps: - uses: actions/checkout@v4 @@ -20,11 +23,11 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r test-requirements.txt + #- name: Install dependencies + # run: | + # python -m pip install --upgrade pip + # pip install -r requirements.txt + # pip install -r test-requirements.txt - name: Run tests env: DETECTLANGUAGE_API_KEY: ${{ secrets.DETECTLANGUAGE_API_KEY }} From 4cd5b3c9b479916919e3d6eacb89837c4597de01 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:17:21 +0300 Subject: [PATCH 04/21] Adjust github test action --- .github/workflows/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bfa6c9e..32f0a46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,8 +6,6 @@ jobs: strategy: matrix: python-version: - - '3.6' - - '3.7' - '3.8' - '3.9' - '3.10' @@ -15,7 +13,6 @@ jobs: - '3.12' - '3.13' - '3.14' - - '3.15' name: Python ${{ matrix.python-version }} sample steps: - uses: actions/checkout@v4 From d2809c00b405ce702bcfda7156d1236ec15f6caa Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:18:30 +0300 Subject: [PATCH 05/21] Adjust github test action --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32f0a46..d3f09f6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,11 +20,11 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' - #- name: Install dependencies - # run: | - # python -m pip install --upgrade pip - # pip install -r requirements.txt - # pip install -r test-requirements.txt + - name: Install dependencies + run: | + # python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r test-requirements.txt - name: Run tests env: DETECTLANGUAGE_API_KEY: ${{ secrets.DETECTLANGUAGE_API_KEY }} From b3538eff0bc18685a643a65f997a4ece5c9fc2ce Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:20:10 +0300 Subject: [PATCH 06/21] Adjust github test action --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d3f09f6..823e126 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: - '3.11' - '3.12' - '3.13' - - '3.14' + # - '3.14' name: Python ${{ matrix.python-version }} sample steps: - uses: actions/checkout@v4 From e6e181bea00e4e5e69e397d72b44355a5e2d35f9 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:22:27 +0300 Subject: [PATCH 07/21] Adjust github test action --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 823e126..6517b0e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: - '3.11' - '3.12' - '3.13' - # - '3.14' + - '3.14.0-beta.4' name: Python ${{ matrix.python-version }} sample steps: - uses: actions/checkout@v4 @@ -22,7 +22,6 @@ jobs: cache: 'pip' - name: Install dependencies run: | - # python -m pip install --upgrade pip pip install -r requirements.txt pip install -r test-requirements.txt - name: Run tests From 56a172531eb471c0505c39565e51dbd22635aae7 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:24:15 +0300 Subject: [PATCH 08/21] Adjust github test action --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6517b0e..edb30de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,6 +6,8 @@ jobs: strategy: matrix: python-version: + - '3.6.7' + - '3.7.1' - '3.8' - '3.9' - '3.10' From 024ed9e1cfc5264331b697b8805d899ec52ed8a6 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:25:16 +0300 Subject: [PATCH 09/21] Adjust github test action --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index edb30de..0b0f083 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,6 @@ jobs: strategy: matrix: python-version: - - '3.6.7' - '3.7.1' - '3.8' - '3.9' From 574305d224c807f01d1898863f84265a9b347dde Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:28:01 +0300 Subject: [PATCH 10/21] Adjust github test action --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b0f083..b9f8f1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: strategy: matrix: python-version: - - '3.7.1' + - '3.7.17' - '3.8' - '3.9' - '3.10' From 832b426f86d2f55b6b78b0be09a3803456317e51 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:28:36 +0300 Subject: [PATCH 11/21] Adjust github test action --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b9f8f1d..6517b0e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,6 @@ jobs: strategy: matrix: python-version: - - '3.7.17' - '3.8' - '3.9' - '3.10' From c318750336ae7530ca401a708eca990e4ba44d0e Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:39:35 +0300 Subject: [PATCH 12/21] Switch to v3 api --- detectlanguage/__init__.py | 2 +- detectlanguage/api.py | 15 ++++++++++----- detectlanguage/client.py | 5 +---- detectlanguage/configuration.py | 3 +-- tests/test_api.py | 24 +++++++++++------------- tests/test_detectlanguage.py | 3 +-- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/detectlanguage/__init__.py b/detectlanguage/__init__.py index bbec5ef..356d8e9 100644 --- a/detectlanguage/__init__.py +++ b/detectlanguage/__init__.py @@ -3,7 +3,7 @@ from .exceptions import * from .configuration import Configuration from .client import Client -from .api import simple_detect, detect, user_status, languages +from .api import detect, detect_code, detect_batch, account_status, languages configuration = Configuration() client = Client(configuration) diff --git a/detectlanguage/api.py b/detectlanguage/api.py index c4defa5..af801b8 100644 --- a/detectlanguage/api.py +++ b/detectlanguage/api.py @@ -1,15 +1,20 @@ import detectlanguage def detect(data): - result = detectlanguage.client.post('detect', { 'q': data }) - return result['data']['detections'] + if isinstance(data, list): + raise ValueError('use detect_batch instead for multiple texts') -def simple_detect(data): + return detectlanguage.client.post('detect', { 'q': data }) + +def detect_code(data): result = detect(data) return result[0]['language'] -def user_status(): - return detectlanguage.client.get('user/status') +def detect_batch(data): + return detectlanguage.client.post('detect-batch', { 'q': data }) + +def account_status(): + return detectlanguage.client.get('account/status') def languages(): return detectlanguage.client.get('languages') diff --git a/detectlanguage/client.py b/detectlanguage/client.py index 999ca45..d37c08f 100644 --- a/detectlanguage/client.py +++ b/detectlanguage/client.py @@ -42,10 +42,7 @@ def handle_http_error(self, r, err): raise DetectLanguageError(err) def url(self, path): - return "%s://%s/%s/%s" % (self.protocol(), self.configuration.host, self.configuration.api_version, path) - - def protocol(self): - return 'https' if self.configuration.secure else 'http' + return "https://%s/%s/%s" % (self.configuration.host, self.configuration.api_version, path) def headers(self): return { diff --git a/detectlanguage/configuration.py b/detectlanguage/configuration.py index 2fb0482..4272ae1 100644 --- a/detectlanguage/configuration.py +++ b/detectlanguage/configuration.py @@ -2,8 +2,7 @@ class Configuration: api_key = None - api_version = '0.2' + api_version = 'v3' host = 'ws.detectlanguage.com' user_agent = 'Detect Language API Python Client ' + detectlanguage.__version__ - secure = False timeout = 5 diff --git a/tests/test_api.py b/tests/test_api.py index aacc206..b648972 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -8,36 +8,34 @@ class TestApi: def setup_method(self): detectlanguage.configuration.api_key = os.environ['DETECTLANGUAGE_API_KEY'] - def test_simple_detect(self): - result = detectlanguage.simple_detect("Hello world") + def test_detect_code(self): + result = detectlanguage.detect_code("Hello world") assert result == 'en' def test_detect(self): result = detectlanguage.detect("Hello world") assert result[0]['language'] == 'en' + def test_detect_with_array(self): + with pytest.raises(ValueError): + detectlanguage.detect(["Hello world", "Ėjo ežiukas"]) + def test_detect_unicode(self): result = detectlanguage.detect("Ėjo ežiukas") assert result[0]['language'] == 'lt' - def test_detect_array(self): - result = detectlanguage.detect(["Hello world", "Ėjo ežiukas"]) + def test_detect_batch(self): + result = detectlanguage.detect_batch(["Hello world", "Ėjo ežiukas"]) assert result[0][0]['language'] == 'en' assert result[1][0]['language'] == 'lt' - def test_user_status(self): - result = detectlanguage.user_status() + def test_account_status(self): + result = detectlanguage.account_status() assert result['status'] == 'ACTIVE' def test_languages(self): result = detectlanguage.languages() - assert { 'code': 'en', 'name': 'ENGLISH' } in result - - def test_secure(self): - detectlanguage.configuration.secure = True - result = detectlanguage.detect("Hello world") - assert result[0]['language'] == 'en' - detectlanguage.configuration.secure = False + assert { 'code': 'en', 'name': 'English' } in result class TestApiErrors: def test_invalid_key(self): diff --git a/tests/test_detectlanguage.py b/tests/test_detectlanguage.py index 6f97109..c91be46 100644 --- a/tests/test_detectlanguage.py +++ b/tests/test_detectlanguage.py @@ -3,9 +3,8 @@ class TestDetectlanguage: def testDefaults(self): - assert detectlanguage.configuration.api_version == '0.2' + assert detectlanguage.configuration.api_version == 'v3' assert detectlanguage.configuration.host == 'ws.detectlanguage.com' - assert detectlanguage.configuration.secure == False def testConfiguration(self): detectlanguage.configuration.api_key = 'TEST' From 8e6f5f9908f839f0f4dfe88e7ca18bd51cc52a9c Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 20:57:44 +0300 Subject: [PATCH 13/21] Add proxy support --- README.md | 4 +-- detectlanguage/client.py | 4 +-- detectlanguage/configuration.py | 1 + tests/test_proxy.py | 47 +++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/test_proxy.py diff --git a/README.md b/README.md index 2b02761..866e434 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ import detectlanguage detectlanguage.configuration.api_key = "YOUR API KEY" -# Enable secure mode (SSL) if you are passing sensitive data -# detectlanguage.configuration.secure = True +# You can use proxy if needed +# detectlanguage.configuration.proxies = {'https': 'https://user:pass@proxy.example.com:8080'} ``` ## Usage diff --git a/detectlanguage/client.py b/detectlanguage/client.py index d37c08f..6f6eae5 100644 --- a/detectlanguage/client.py +++ b/detectlanguage/client.py @@ -13,11 +13,11 @@ def __init__(self, configuration): self.configuration = configuration def get(self, path, payload = {}): - r = requests.get(self.url(path), params=payload, headers = self.headers(), timeout = self.configuration.timeout) + r = requests.get(self.url(path), params=payload, headers = self.headers(), timeout = self.configuration.timeout, proxies = self.configuration.proxies) return self.handle_response(r) def post(self, path, payload): - r = requests.post(self.url(path), json=payload, headers = self.headers(), timeout = self.configuration.timeout) + r = requests.post(self.url(path), json=payload, headers = self.headers(), timeout = self.configuration.timeout, proxies = self.configuration.proxies) return self.handle_response(r) def handle_response(self, r): diff --git a/detectlanguage/configuration.py b/detectlanguage/configuration.py index 4272ae1..173ddee 100644 --- a/detectlanguage/configuration.py +++ b/detectlanguage/configuration.py @@ -6,3 +6,4 @@ class Configuration: host = 'ws.detectlanguage.com' user_agent = 'Detect Language API Python Client ' + detectlanguage.__version__ timeout = 5 + proxies = None # e.g., {'https': 'https://proxy.example.com:8080'} diff --git a/tests/test_proxy.py b/tests/test_proxy.py new file mode 100644 index 0000000..fd7f936 --- /dev/null +++ b/tests/test_proxy.py @@ -0,0 +1,47 @@ +import unittest +from unittest.mock import patch, MagicMock +import detectlanguage + + +class TestProxyConfiguration(unittest.TestCase): + def setUp(self): + detectlanguage.configuration.proxies = None + + def test_proxy_configuration(self): + """Test proxy configuration""" + detectlanguage.configuration.proxies = {'https': 'https://proxy.example.com:8080'} + self.assertEqual(detectlanguage.configuration.proxies, {'https': 'https://proxy.example.com:8080'}) + + @patch('requests.get') + def test_client_uses_proxy(self, mock_get): + """Test that client uses configured proxy""" + detectlanguage.configuration.proxies = {'https': 'https://proxy.example.com:8080'} + + mock_response = MagicMock() + mock_response.json.return_value = {'test': 'data'} + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + detectlanguage.account_status() + + mock_get.assert_called_once() + self.assertEqual(mock_get.call_args[1]['proxies'], {'https': 'https://proxy.example.com:8080'}) + + @patch('requests.get') + def test_client_no_proxy_when_disabled(self, mock_get): + """Test that client doesn't use proxy when disabled""" + detectlanguage.configuration.proxies = None + + mock_response = MagicMock() + mock_response.json.return_value = {'test': 'data'} + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + detectlanguage.account_status() + + mock_get.assert_called_once() + self.assertIsNone(mock_get.call_args[1]['proxies']) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 3c25f310a136a64745c8be2184f736c2205193e9 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:11:05 +0300 Subject: [PATCH 14/21] Update readme --- README.md | 24 ++++++++++++------------ mise.toml | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 866e434..164d7da 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ import detectlanguage detectlanguage.configuration.api_key = "YOUR API KEY" # You can use proxy if needed -# detectlanguage.configuration.proxies = {'https': 'https://user:pass@proxy.example.com:8080'} +# detectlanguage.configuration.proxies = {'https': 'https://user:pass@proxy:8080'} ``` ## Usage @@ -31,37 +31,36 @@ detectlanguage.configuration.api_key = "YOUR API KEY" ### Language detection ```python -detectlanguage.detect("Buenos dias señor") +detectlanguage.detect("Dolce far niente") ``` #### Result ```python -[{'isReliable': True, 'confidence': 12.04, 'language': 'es'}] +[{'language': 'it', 'score': 0.5074}] ``` ### Simple language detection -If you need just a language code you can use `simple_detect`. It returns just the language code. +If you need just a language code you can use `detect_code`. ```python -detectlanguage.simple_detect("Buenos dias señor") +detectlanguage.detect_code("Dolce far niente") ``` #### Result ```python -'es' +'it' ``` ### Batch detection It is possible to detect language of several texts with one request. This method is faster than doing one request per text. -To use batch detection just pass array of texts to `detect` method. ```python -detectlanguage.detect(["Buenos dias señor", "Labas rytas"]) +detectlanguage.detect_batch(["Dolce far niente", "Hello world"]) ``` #### Result @@ -69,14 +68,13 @@ detectlanguage.detect(["Buenos dias señor", "Labas rytas"]) Result is array of detections in the same order as the texts were passed. ```python -[ [ {'isReliable': True, 'confidence': 12.04, 'language': 'es'} ], - [ {'isReliable': True, 'confidence': 9.38, 'language': 'lt'} ] ] +[[{'language': 'it', 'score': 0.5074}], [{'language': 'en', 'score': 0.9098}]] ``` ### Getting your account status ```python -detectlanguage.user_status() +detectlanguage.account_status() ``` #### Result @@ -95,7 +93,9 @@ detectlanguage.languages() #### Result -Array of language codes and names. +```python +[{'code': 'aa', 'name': 'Afar'}, {'code': 'ab', 'name': 'Abkhazian'}, ...] +``` ## Contribution diff --git a/mise.toml b/mise.toml index df9607b..2c2e968 100644 --- a/mise.toml +++ b/mise.toml @@ -9,3 +9,4 @@ test = "pytest" test-cov = "pytest --cov=detectlanguage --cov-report=term-missing" test-verbose = "pytest -v" install-dev = "pip install -r requirements.txt && pip install -r test-requirements.txt" +console = "python -c \"import detectlanguage; import code; code.interact(local=locals())\"" From e6256d220b675c21b9cefb2a7294989c0c45a18e Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:13:26 +0300 Subject: [PATCH 15/21] Adjust readme --- README.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 164d7da..3aa0c53 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ detectlanguage.detect("Dolce far niente") [{'language': 'it', 'score': 0.5074}] ``` -### Simple language detection +### Simple code detection If you need just a language code you can use `detect_code`. @@ -85,7 +85,7 @@ detectlanguage.account_status() 'plan_expires': None } ``` -### Getting list detectable languages +### Getting list of supported languages ```python detectlanguage.languages() @@ -97,16 +97,6 @@ detectlanguage.languages() [{'code': 'aa', 'name': 'Afar'}, {'code': 'ab', 'name': 'Abkhazian'}, ...] ``` -## Contribution - -You are welcome to patch and send GitHub pull requests. - -### Testing - - pip install -r requirements.txt - pip install -r test-requirements.txt - pytest - ## License Detect Language API Python Client is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. From 9b6adcfba94a7c60628ea844ca32366a2ab4ebfc Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:18:11 +0300 Subject: [PATCH 16/21] Adjust readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3aa0c53..9990144 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ detectlanguage.configuration.api_key = "YOUR API KEY" ## Usage -### Language detection +### Detect language ```python detectlanguage.detect("Dolce far niente") @@ -40,7 +40,7 @@ detectlanguage.detect("Dolce far niente") [{'language': 'it', 'score': 0.5074}] ``` -### Simple code detection +### Detect single code If you need just a language code you can use `detect_code`. @@ -71,7 +71,7 @@ Result is array of detections in the same order as the texts were passed. [[{'language': 'it', 'score': 0.5074}], [{'language': 'en', 'score': 0.9098}]] ``` -### Getting your account status +### Get your account status ```python detectlanguage.account_status() @@ -85,7 +85,7 @@ detectlanguage.account_status() 'plan_expires': None } ``` -### Getting list of supported languages +### Get list of supported languages ```python detectlanguage.languages() From 750352cf82f455c67da39e1889641a0d63fc593a Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:24:59 +0300 Subject: [PATCH 17/21] Add changelog --- CHANGELOG.md | 21 +++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 25 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..52956a4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## v2.0.0 + +### Added +- `detect_batch` method for batch detections +- Proxy support + +### Changed +- Switched to v3 API which uses updated language detection model +- ⚠️ `simple_detect` method renamed to `detect_code` +- ⚠️ `detect` method result fields are `language` and `score` +- ⚠️ `detect` method no longer accept arrays - use `detect_batch` instead +- ⚠️ `user_status` method renamed to `account_status` +- HTTPS is used by default. Removed secure mode configuration. diff --git a/README.md b/README.md index 9990144..17de63b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ You can get it by signing up at https://detectlanguage.com pip install detectlanguage ``` +### Upgrading + +When upgrading please check [changelog](CHANGELOG.md) for breaking changes. + ### Configuration ```python From e04b251aeddf3cd4b779f11a48ab99510b115d7e Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:25:54 +0300 Subject: [PATCH 18/21] Bump version --- detectlanguage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detectlanguage/__init__.py b/detectlanguage/__init__.py index 356d8e9..938d3f6 100644 --- a/detectlanguage/__init__.py +++ b/detectlanguage/__init__.py @@ -1,4 +1,4 @@ -__version__ = '1.5.0' +__version__ = '2.0.0' from .exceptions import * from .configuration import Configuration From ad9e09ba0f17194b0ddadb90edd18f66490014da Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 21:53:55 +0300 Subject: [PATCH 19/21] Add publish action --- .github/workflows/publish.yml | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..3056e77 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,53 @@ +name: Publish Python 🐍 distribution 📦 to PyPI + +on: + release: + types: [published] + +jobs: + build: + name: Build distribution 📦 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install pypa/build + run: >- + python3 -m + pip install + build + --user + - name: Build a binary wheel and a source tarball + run: python3 -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + publish-to-pypi: + name: >- + Publish Python 🐍 distribution 📦 to PyPI + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/detectlanguage + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 From 821ecdff1b4a97db3dc7d296ab9b519e067bd418 Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sun, 20 Jul 2025 22:13:41 +0300 Subject: [PATCH 20/21] Adjust build --- mise.toml | 3 ++- setup.cfg | 3 +-- setup.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mise.toml b/mise.toml index 2c2e968..7902ecf 100644 --- a/mise.toml +++ b/mise.toml @@ -5,8 +5,9 @@ _.file = ".env.local" _.python.venv = { path = ".venv", create = true } [tasks] +install-dev = "pip install -r requirements.txt && pip install -r test-requirements.txt && pip install build" test = "pytest" test-cov = "pytest --cov=detectlanguage --cov-report=term-missing" test-verbose = "pytest -v" -install-dev = "pip install -r requirements.txt && pip install -r test-requirements.txt" console = "python -c \"import detectlanguage; import code; code.interact(local=locals())\"" +build = "python3 -m build" diff --git a/setup.cfg b/setup.cfg index 8c28267..08aedd7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,2 @@ [metadata] -description-file = README.md - +description_file = README.md diff --git a/setup.py b/setup.py index 78a3200..feb7ae1 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,6 @@ download_url = 'https://github.com/detectlanguage/detectlanguage-python', keywords = ['language', 'identification', 'detection', 'api', 'client'], install_requires= ['requests>=2.4.2'], - test_requires= ['pytest>=7.0.0', 'pytest-mock>=3.10.0', 'pytest-cov>=4.0.0'], classifiers = [], license = 'MIT', ) From d85f5fab9cb70a17e20ecce0d05d367efd57bb0e Mon Sep 17 00:00:00 2001 From: Laurynas Butkus Date: Sat, 26 Jul 2025 10:04:07 +0300 Subject: [PATCH 21/21] Add deprecations --- CHANGELOG.md | 12 ++++++---- detectlanguage/__init__.py | 3 +++ detectlanguage/api.py | 46 +++++++++++++++++++++++++++++++++++++- tests/test_api.py | 15 ++++++++++--- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52956a4..1a40650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Switched to v3 API which uses updated language detection model -- ⚠️ `simple_detect` method renamed to `detect_code` - ⚠️ `detect` method result fields are `language` and `score` -- ⚠️ `detect` method no longer accept arrays - use `detect_batch` instead -- ⚠️ `user_status` method renamed to `account_status` -- HTTPS is used by default. Removed secure mode configuration. + +### Deprecated +- Calling `detect()` with list argument. Use `detect_batch` instead. +- `simple_detect()` - Use `detect_code()` instead. Will be removed in a future version. +- `user_status()` - Use `account_status()` instead. Will be removed in a future version. + +### Removed +- Secure mode configuration. HTTPS is used by default. diff --git a/detectlanguage/__init__.py b/detectlanguage/__init__.py index 938d3f6..ca8c3fc 100644 --- a/detectlanguage/__init__.py +++ b/detectlanguage/__init__.py @@ -5,5 +5,8 @@ from .client import Client from .api import detect, detect_code, detect_batch, account_status, languages +# deprecated functions +from .api import simple_detect, user_status + configuration = Configuration() client = Client(configuration) diff --git a/detectlanguage/api.py b/detectlanguage/api.py index af801b8..37a7681 100644 --- a/detectlanguage/api.py +++ b/detectlanguage/api.py @@ -1,8 +1,10 @@ import detectlanguage +import warnings def detect(data): if isinstance(data, list): - raise ValueError('use detect_batch instead for multiple texts') + _warn_deprecated('use detect_batch instead for multiple texts') + return detect_batch(data) return detectlanguage.client.post('detect', { 'q': data }) @@ -18,3 +20,45 @@ def account_status(): def languages(): return detectlanguage.client.get('languages') + + +### DEPRECATED + +def simple_detect(data): + """ + DEPRECATED: This function is deprecated and will be removed in a future version. + Use detect_code() instead. + + Args: + data: Text to detect language for + + Returns: + str: Language code of the detected language + """ + _warn_deprecated( + "simple_detect() is deprecated and will be removed in a future version. " + "Use detect_code() instead." + ) + return detect_code(data) + +def user_status(): + """ + DEPRECATED: This function is deprecated and will be removed in a future version. + Use account_status() instead. + + Returns: + dict: Account status information + """ + _warn_deprecated( + "user_status() is deprecated and will be removed in a future version. " + "Use account_status() instead." + ) + return account_status() + +def _warn_deprecated(message): + """Internal utility function to emit deprecation warnings.""" + warnings.warn( + message, + DeprecationWarning, + stacklevel=2 + ) diff --git a/tests/test_api.py b/tests/test_api.py index b648972..189f67a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -7,7 +7,7 @@ class TestApi: def setup_method(self): detectlanguage.configuration.api_key = os.environ['DETECTLANGUAGE_API_KEY'] - + def test_detect_code(self): result = detectlanguage.detect_code("Hello world") assert result == 'en' @@ -17,7 +17,7 @@ def test_detect(self): assert result[0]['language'] == 'en' def test_detect_with_array(self): - with pytest.raises(ValueError): + with pytest.warns(DeprecationWarning, match="use detect_batch"): detectlanguage.detect(["Hello world", "Ėjo ežiukas"]) def test_detect_unicode(self): @@ -37,9 +37,18 @@ def test_languages(self): result = detectlanguage.languages() assert { 'code': 'en', 'name': 'English' } in result + def test_simple_detect(self): + with pytest.warns(DeprecationWarning, match="simple_detect.*deprecated"): + result = detectlanguage.simple_detect("Hello world") + assert result == 'en' + + def test_user_status(self): + with pytest.warns(DeprecationWarning, match="user_status.*deprecated"): + result = detectlanguage.user_status() + assert result['status'] == 'ACTIVE' + class TestApiErrors: def test_invalid_key(self): detectlanguage.configuration.api_key = 'invalid' with pytest.raises(detectlanguage.DetectLanguageError): detectlanguage.detect("Hello world") - \ No newline at end of file