diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbd5824..b49867d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,10 +4,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.6' + python-version: '3.9' - name: Install requirements run: pip install flake8 pycodestyle - name: Check syntax @@ -17,16 +17,26 @@ jobs: needs: lint strategy: matrix: - ckan-version: [2.8, 2.9, 2.9-py2] + include: + - ckan-version: "2.11" + ckan-image: "ckan/ckan-dev:2.11-py3.10" + solr-version: "9" + - ckan-version: "2.10" + ckan-image: "ckan/ckan-dev:2.10-py3.10" + solr-version: "9" + - ckan-version: "2.9" + ckan-image: "ckan/ckan-dev:2.9-py3.9" + solr-version: "8" fail-fast: false name: CKAN ${{ matrix.ckan-version }} runs-on: ubuntu-latest container: - image: openknowledge/ckan-dev:${{ matrix.ckan-version }} + image: ${{ matrix.ckan-image }} + options: --user root services: solr: - image: ckan/ckan-solr:${{ matrix.ckan-version }} + image: ckan/ckan-solr:${{ matrix.ckan-version }}-solr${{ matrix.solr-version }} postgres: image: ckan/ckan-postgres-dev:${{ matrix.ckan-version }} env: @@ -44,34 +54,16 @@ jobs: CKAN_REDIS_URL: redis://redis:6379/1 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v3 - - name: Install requirements (Python 3) - if: ${{ matrix.ckan-version != '2.7' && matrix.ckan-version != '2.8' && matrix.ckan-version != '2.9-py2'}} + - uses: actions/checkout@v4 + - name: Install requirements (common) run: | - pip install cython - pip install cchardet pip install -r requirements.txt pip install -r dev-requirements.txt - - name: Install requirements (Python 2) - if: ${{ matrix.ckan-version == '2.7' || matrix.ckan-version == '2.8' || matrix.ckan-version == '2.9-py2'}} - run: | - pip install cython - pip install cchardet - pip install -r requirements-py2.txt - pip install -r dev-requirements-py2.txt - - name: Install requirements (common) - run: | pip install -e . # Replace default path to CKAN core config file with the one on the container sed -i -e 's/use = config:.*/use = config:\/srv\/app\/src\/ckan\/test-core.ini/' test.ini - - name: Setup extension (CKAN >= 2.9) - if: ${{ matrix.ckan-version != '2.7' && matrix.ckan-version != '2.8' }} + - name: Setup extension run: | ckan -c test.ini db init - - name: Setup extension (CKAN < 2.9) - if: ${{ matrix.ckan-version == '2.7' || matrix.ckan-version == '2.8' }} - run: | - paster --plugin=ckan db init -c test.ini - name: Run tests - run: pytest --ckan-ini=test.ini --cov=ckanext.datapackager --cov-report=xml --cov-append --disable-warnings ckanext/datapackager/tests + run: pytest --ckan-ini=test.ini --cov=ckanext.datapackager --cov-report=term-missing --cov-append --disable-warnings ckanext/datapackager/tests diff --git a/README.md b/README.md index 6ace0e5..5d2b9a8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This extension adds importing and exporting of [Data Packages][data-packages] to ## Requirements -* CKAN >= 2.8 +* CKAN >= 2.9 ## Installing @@ -128,14 +128,10 @@ For instance You'll need to install the dev requirements to run the tests: -To run the tests on CKAN >= 2.9, do: +To run the tests, do: pytest --ckan-ini=test.ini ckanext/dcat/tests -To run the tests on CKAN <= 2.8, do: - - nosetests --nologcapture --ckan --with-pylons=test-nose.ini ckanext/dcat/tests/nose - Note that ckanext-datapackager's `test.ini` file assumes that the relative path from it to CKAN's `test-core.ini` file is `../ckan/test-core.ini`, i.e. that you have CKAN and ckanext-datapackager installed next to each other in the same directory. This diff --git a/ckanext/datapackager/controllers/datapackage.py b/ckanext/datapackager/controllers/datapackage.py index 7f186fd..465034f 100644 --- a/ckanext/datapackager/controllers/datapackage.py +++ b/ckanext/datapackager/controllers/datapackage.py @@ -22,7 +22,7 @@ def new(data=None, errors=None, error_summary=None): errors = errors or {} error_summary = error_summary or {} default_data = { - 'owner_org': toolkit.request.params.get('group'), + 'owner_org': toolkit.request.args.get('group'), } data = data or default_data @@ -50,8 +50,8 @@ def import_datapackage(): else: params = toolkit.request.params - if toolkit.check_ckan_version(min_version="2.9"): - if 'upload' in toolkit.request.files: + if 'upload' in toolkit.request.files: + if toolkit.request.files['upload'].filename: params['upload'] = toolkit.request.files['upload'] dataset = toolkit.get_action('package_create_from_datapackage')( @@ -59,10 +59,7 @@ def import_datapackage(): params, ) - if toolkit.check_ckan_version(min_version="2.9"): - return toolkit.redirect_to('dataset.read', id=dataset['name']) - else: - return toolkit.redirect_to('dataset_read', id=dataset['name']) + return toolkit.redirect_to('dataset.read', id=dataset['name']) except toolkit.ValidationError as e: errors = e.error_dict @@ -81,7 +78,7 @@ def export_datapackage(package_id): 'user': toolkit.c.user, } - r = make_response() if toolkit.check_ckan_version(min_version="2.9") else toolkit.response + r = make_response() r.content_disposition = 'attachment; filename=datapackage.json'.format( package_id) r.content_type = 'application/json' @@ -94,20 +91,5 @@ def export_datapackage(package_id): except toolkit.ObjectNotFound: return toolkit.abort(404, 'Dataset not found') - if toolkit.check_ckan_version(min_version="2.9"): - r.data = json.dumps(datapackage_dict, indent=2) - return r - else: - return json.dumps(datapackage_dict, indent=2) - - -if not toolkit.check_ckan_version(u'2.9'): - class DataPackageController(toolkit.BaseController): - def new(self, data=None, errors=None, error_summary=None): - return new(data, errors, error_summary) - def import_datapackage(self): - return import_datapackage() - def export_datapackage(self, package_id): - return export_datapackage(package_id) - - + r.data = json.dumps(datapackage_dict, indent=2) + return r diff --git a/ckanext/datapackager/logic/action/create.py b/ckanext/datapackager/logic/action/create.py index b0f057b..9168ee1 100644 --- a/ckanext/datapackager/logic/action/create.py +++ b/ckanext/datapackager/logic/action/create.py @@ -1,10 +1,7 @@ import random -import cgi import json import tempfile -import six - import ckan.plugins.toolkit as toolkit from frictionless_ckan_mapper import frictionless_to_ckan as converter from werkzeug.datastructures import FileStorage @@ -76,7 +73,7 @@ def package_create_from_datapackage(context, data_dict): toolkit.get_action('package_delete')( context, {'id': dataset_id}) except Exception as e2: - six.raise_from(e, e2) + raise e from e2 else: raise e @@ -88,10 +85,7 @@ def _load_and_validate_datapackage(url=None, upload=None): try: if _upload_attribute_is_valid(upload): - if toolkit.check_ckan_version(min_version="2.9"): - dp = datapackage.DataPackage(upload) - else: - dp = datapackage.DataPackage(upload.file) + dp = datapackage.DataPackage(upload) else: dp = datapackage.DataPackage(url) @@ -151,14 +145,11 @@ def _create_and_upload_resource_with_inline_data(context, resource): data = resource['data'] del resource['data'] - if not isinstance(data, six.string_types): + if not isinstance(data, str): data = json.dumps(data, indent=2) with tempfile.NamedTemporaryFile(prefix=prefix) as f: - if six.PY3: - f.write(six.binary_type(data, 'utf-8')) - else: - f.write(six.binary_type(data)) + f.write(bytes(data, 'utf-8')) f.seek(0) _create_and_upload_resource(context, resource, f) @@ -183,21 +174,10 @@ def _create_and_upload_local_resource(context, resource): def _create_and_upload_resource(context, resource, the_file): resource['url'] = 'url' resource['url_type'] = 'upload' - - if toolkit.check_ckan_version(min_version="2.9"): - resource['upload'] = FileStorage(the_file, the_file.name, the_file.name) - else: - resource['upload'] = _UploadLocalFileStorage(the_file) + resource['upload'] = FileStorage(the_file, the_file.name, the_file.name) toolkit.get_action('resource_create')(context, resource) def _upload_attribute_is_valid(upload): return hasattr(upload, 'read') or hasattr(upload, 'file') and hasattr(upload.file, 'read') - -# Used only in CKAN < 2.9 -class _UploadLocalFileStorage(cgi.FieldStorage): - def __init__(self, fp, *args, **kwargs): - self.name = fp.name - self.filename = fp.name - self.file = fp diff --git a/ckanext/datapackager/plugin/__init__.py b/ckanext/datapackager/plugin/__init__.py index 775d4e7..1902d69 100644 --- a/ckanext/datapackager/plugin/__init__.py +++ b/ckanext/datapackager/plugin/__init__.py @@ -1,21 +1,18 @@ +from flask import Blueprint + import ckan.plugins as plugins import ckan.plugins.toolkit as toolkit +from ckanext.datapackager.controllers import datapackage from ckanext.datapackager.logic.action.create import package_create_from_datapackage from ckanext.datapackager.logic.action.get import package_show_as_datapackage -if toolkit.check_ckan_version(u'2.9'): - from ckanext.datapackager.plugin.flask_plugin import MixinPlugin - ckan_29_or_higher = True -else: - from ckanext.datapackager.plugin.pylons_plugin import MixinPlugin - ckan_29_or_higher = False - -class DataPackagerPlugin(MixinPlugin, plugins.SingletonPlugin): +class DataPackagerPlugin(plugins.SingletonPlugin): '''Plugin that adds importing/exporting datasets as Data Packages. ''' plugins.implements(plugins.IActions) plugins.implements(plugins.IConfigurer) + plugins.implements(plugins.IBlueprint) def update_config(self, config): toolkit.add_template_directory(config, '../templates') @@ -25,3 +22,26 @@ def get_actions(self): 'package_create_from_datapackage': package_create_from_datapackage, 'package_show_as_datapackage': package_show_as_datapackage, } + + def get_blueprint(self): + blueprint = Blueprint("datapackager", __name__) + # As long as the URL for import_datapackage_view and import_datapackage are the same, reverse lookups from import_datapackage will work + blueprint.add_url_rule( + "/import_datapackage", + view_func=datapackage.new, + endpoint="import_datapackage", + methods=["GET"], + ) + blueprint.add_url_rule( + "/import_datapackage", + view_func=datapackage.import_datapackage, + endpoint="import_datapackage_post", + methods=["POST"], + ) + blueprint.add_url_rule( + "/dataset//datapackage.json", + view_func=datapackage.export_datapackage, + endpoint="export_datapackage", + methods=["GET"], + ) + return blueprint diff --git a/ckanext/datapackager/plugin/flask_plugin.py b/ckanext/datapackager/plugin/flask_plugin.py deleted file mode 100644 index b9dc615..0000000 --- a/ckanext/datapackager/plugin/flask_plugin.py +++ /dev/null @@ -1,15 +0,0 @@ -import ckan.plugins as plugins - -from flask import Blueprint -import ckanext.datapackager.controllers.datapackage as datapackage - -class MixinPlugin(plugins.SingletonPlugin): - plugins.implements(plugins.IBlueprint) - - def get_blueprint(self): - blueprint = Blueprint('datapackager', __name__) - # As long as the URL for import_datapackage_view and import_datapackage are the same, reverse lookups from import_datapackage will work - blueprint.add_url_rule("/import_datapackage", view_func=datapackage.new, endpoint='import_datapackage', methods=['GET']) - blueprint.add_url_rule("/import_datapackage", view_func=datapackage.import_datapackage, endpoint='import_datapackage_post', methods=['POST']) - blueprint.add_url_rule("/dataset//datapackage.json", view_func=datapackage.export_datapackage, endpoint='export_datapackage', methods=['GET']) - return blueprint \ No newline at end of file diff --git a/ckanext/datapackager/plugin/pylons_plugin.py b/ckanext/datapackager/plugin/pylons_plugin.py deleted file mode 100644 index 156520d..0000000 --- a/ckanext/datapackager/plugin/pylons_plugin.py +++ /dev/null @@ -1,27 +0,0 @@ -import ckan.plugins as plugins - -class MixinPlugin(plugins.SingletonPlugin): - plugins.implements(plugins.IRoutes, inherit=True) - - def before_map(self, map_): - map_.connect( - 'import_datapackage', - '/import_datapackage', - controller='ckanext.datapackager.controllers.datapackage:DataPackageController', - action='new', - conditions=dict(method=['GET']), - ) - map_.connect( - 'import_datapackage', - '/import_datapackage', - controller='ckanext.datapackager.controllers.datapackage:DataPackageController', - action='import_datapackage', - conditions=dict(method=['POST']), - ) - map_.connect( - 'export_datapackage', - '/dataset/{package_id}/datapackage.json', - controller='ckanext.datapackager.controllers.datapackage:DataPackageController', - action='export_datapackage' - ) - return map_ diff --git a/ckanext/datapackager/templates/datapackage/import_datapackage.html b/ckanext/datapackager/templates/datapackage/import_datapackage.html index cc78ad8..2c86b6c 100644 --- a/ckanext/datapackager/templates/datapackage/import_datapackage.html +++ b/ckanext/datapackager/templates/datapackage/import_datapackage.html @@ -3,18 +3,13 @@ {% block subtitle %}{{ _('Import Data Package') }}{% endblock %} {% block breadcrumb_content %} - {% if h.ckan_version().split('.')|map('int')|list >= [2, 9, 0] %} -
  • {% link_for _('Datasets'), named_route='dataset.search' %}
  • - {% else %} -
  • {% link_for _('Datasets'), controller='package', action='search' %}
  • - {% endif %} - +
  • {% link_for _('Datasets'), named_route='dataset.search' %}
  • {{ _('Import Data Package') }}
  • {% endblock %} {% block info_module %}
    -

    {{ _('What are Data Packages?') }}

    +

    {{ _('What are Data Packages?') }}

    {% trans %} diff --git a/ckanext/datapackager/templates/datapackage/snippets/import_datapackage_form.html b/ckanext/datapackager/templates/datapackage/snippets/import_datapackage_form.html index 546031c..f21633e 100644 --- a/ckanext/datapackager/templates/datapackage/snippets/import_datapackage_form.html +++ b/ckanext/datapackager/templates/datapackage/snippets/import_datapackage_form.html @@ -1,7 +1,8 @@ {% import 'macros/form.html' as form %} -{% set action = h.url_for('datapackager.import_datapackage') if h.ckan_version().split('.')|map('int')|list >= [2, 9, 0] else h.url_for('import_datapackage') %} +{% set action = h.url_for('datapackager.import_datapackage') %}

    + {{ h.csrf_input() if 'csrf_input' in h }} {% block errors %}{{ form.errors(error_summary) }}{% endblock %} {% block basic_fields %} diff --git a/ckanext/datapackager/templates/organization/read.html b/ckanext/datapackager/templates/organization/read.html index 2b2b084..9f6ca23 100644 --- a/ckanext/datapackager/templates/organization/read.html +++ b/ckanext/datapackager/templates/organization/read.html @@ -3,7 +3,7 @@ {% block page_primary_action %} {{ super() }} {% if h.check_access('package_create', {'owner_org': c.group_dict.id}) %} - + {{ _('Import Data Package') }} diff --git a/ckanext/datapackager/templates/package/read.html b/ckanext/datapackager/templates/package/read.html index 404c8f1..442bca3 100644 --- a/ckanext/datapackager/templates/package/read.html +++ b/ckanext/datapackager/templates/package/read.html @@ -2,14 +2,8 @@ {% block package_resources %}
    - - {% if h.ckan_version().split('.')|map('int')|list >= [2, 9, 0] %} - - {% else %} - - {% endif %} - - Download Data Package + + Download Data Package
    diff --git a/ckanext/datapackager/templates/package/search.html b/ckanext/datapackager/templates/package/search.html index 52a4882..b2d59b7 100644 --- a/ckanext/datapackager/templates/package/search.html +++ b/ckanext/datapackager/templates/package/search.html @@ -4,14 +4,8 @@ {% if h.check_access('package_create') %}
    - {% if h.ckan_version().split('.')|map('int')|list >= [2, 9, 0] %} - {% link_for _('Add Dataset'), named_route='dataset.new', class_='btn btn-primary', icon='plus-sign-alt' %} - {% link_for _('Import Data Package'), named_route='datapackager.import_datapackage', class_='btn', icon='plus-sign-alt' %} - {% else %} - {% link_for _('Add Dataset'), controller='package', action='new', class_='btn btn-primary', icon='plus-sign-alt' %} - {% link_for _('Import Data Package'), named_route='import_datapackage', class_='btn', icon='plus-sign-alt' %} - {% endif %} - + {% snippet 'snippets/add_dataset.html', dataset_type=dataset_type %} + {% link_for _('Import Data Package'), named_route='datapackager.import_datapackage', class_='btn btn-primary', icon='fa fa-upload' %}
    {% endif %} {% endblock %} diff --git a/ckanext/datapackager/templates/user/dashboard_datasets.html b/ckanext/datapackager/templates/user/dashboard_datasets.html index 7af44c3..d9e9bf1 100644 --- a/ckanext/datapackager/templates/user/dashboard_datasets.html +++ b/ckanext/datapackager/templates/user/dashboard_datasets.html @@ -3,7 +3,7 @@ {% block page_primary_action %} {{ super() }} {% if h.check_access('package_create') %} - + {{ _('Import Data Package') }} diff --git a/ckanext/datapackager/tests/controllers/test_datapackage.py b/ckanext/datapackager/tests/controllers/test_datapackage.py index b5bb3a9..a86882d 100644 --- a/ckanext/datapackager/tests/controllers/test_datapackage.py +++ b/ckanext/datapackager/tests/controllers/test_datapackage.py @@ -5,7 +5,6 @@ from bs4 import BeautifulSoup import re -import ckanapi import datapackage import ckan.tests.factories as factories import ckan.tests.helpers as helpers @@ -14,27 +13,42 @@ responses.add_passthru(toolkit.config['solr_url']) -def _url_for(*args, **kwargs): - if toolkit.check_ckan_version(max_version="2.9"): - if args[0] == "datapackager.export_datapackage": - args = ("export_datapackage", ) - elif args[0] == "datapackager.import_datapackage": - args = ("import_datapackage", ) - - return toolkit.url_for(*args, **kwargs) +@pytest.fixture +def sysadmin_env(): + try: + from ckan.tests.factories import SysadminWithToken + user = SysadminWithToken() + return {"Authorization": user["token"]} + except ImportError: + # ckan <= 2.9 + from ckan.tests.factories import Sysadmin + user = Sysadmin() + return {"REMOTE_USER": user["name"].encode("ascii")} + + +@pytest.fixture +def user_env(): + try: + from ckan.tests.factories import UserWithToken + user = UserWithToken() + return {"Authorization": user["token"]} + except ImportError: + # ckan <= 2.9 + from ckan.tests.factories import User + user = User() + return {"REMOTE_USER": user["name"].encode("ascii")} @pytest.mark.ckan_config('ckan.plugins', 'datapackager') -@pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') +@pytest.mark.usefixtures('with_plugins', 'clean_db') class TestDataPackageController(): '''Functional tests for the DataPackageController class.''' - def test_download_datapackage(self, app): + def test_download_datapackage(self, app, sysadmin_env): '''Test downloading a DataPackage file of a package. ''' - user = factories.Sysadmin() dataset = factories.Dataset( maintainer = "John Smith", maintainer_email = "jsmith@email.com", @@ -61,7 +75,7 @@ def test_download_datapackage(self, app): ) # Download the package's JSON file. - url = _url_for('datapackager.export_datapackage', + url = toolkit.url_for('datapackager.export_datapackage', package_id=dataset['name']) response = app.get(url) @@ -91,29 +105,28 @@ def test_that_download_button_is_on_page(self, app): soup = BeautifulSoup(response.body) download_button = soup.find(id='export_datapackage_button') download_url = download_button['href'] - assert download_url == _url_for('datapackager.export_datapackage', + assert download_url == toolkit.url_for('datapackager.export_datapackage', package_id=dataset['id']) - def test_new_renders(self, app): - user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = _url_for('datapackager.import_datapackage') - response = app.get(url, extra_environ=env) - if toolkit.check_ckan_version(min_version="2.9"): - assert 200 == response.status_code + def test_new_renders(self, app, user_env): + url = toolkit.url_for('datapackager.import_datapackage') + if toolkit.check_ckan_version(min_version="2.10.0"): + response = app.get(url, headers=user_env) else: - assert 200 == response.status_int + response = app.get(url, environ_overrides=user_env) + assert 200 == response.status_code @pytest.mark.ckan_config('ckan.auth.create_unowned_dataset', False) - def test_new_requires_user_to_be_able_to_create_packages(self, app): - user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = _url_for('datapackager.import_datapackage') - response = app.get(url, extra_environ=env, status=401) + def test_new_requires_user_to_be_able_to_create_packages(self, app, user_env): + url = toolkit.url_for('datapackager.import_datapackage') + if toolkit.check_ckan_version(min_version="2.10.0"): + response = app.get(url, headers=user_env, status=401) + else: + response = app.get(url, environ_overrides=user_env, status=401) assert 'Unauthorized to create a dataset' in response.body @responses.activate - def test_import_datapackage(self, app): + def test_import_datapackage(self, app, user_env): datapackage_url = 'http://www.foo.com/datapackage.json' datapackage = { 'name': 'foo', @@ -126,23 +139,20 @@ def test_import_datapackage(self, app): } responses.add('GET', datapackage_url, json=datapackage) - user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = _url_for('datapackager.import_datapackage', url=datapackage_url) - if toolkit.check_ckan_version(min_version="2.9"): + url = toolkit.url_for('datapackager.import_datapackage', url=datapackage_url) + if toolkit.check_ckan_version(min_version="2.10.0"): response = app.post( url, - extra_environ=env, + headers=user_env, follow_redirects=False ) - - assert response.status_code == 302 else: response = app.post( url, - extra_environ=env, + environ_overrides=user_env, + follow_redirects=False ) - assert response.status_int == 302 + assert response.status_code == 302 # Should redirect to dataset's page assert re.match('.*/dataset/foo$', response.headers['Location']) diff --git a/ckanext/datapackager/tests/lib/test_converter.py b/ckanext/datapackager/tests/lib/test_converter.py index 747f3fd..7bc9e83 100644 --- a/ckanext/datapackager/tests/lib/test_converter.py +++ b/ckanext/datapackager/tests/lib/test_converter.py @@ -20,6 +20,8 @@ def setUp(self): "title": "Countries GDP", "version": "1.0", "resources": [self.resource_dict], + "license_id": "", + "license_title": "" } def test_basic_dataset_in_setup_is_valid(self): @@ -282,7 +284,7 @@ def test_datapackage_name_title_and_version(self): assert result["name"] == datapackage_dict["name"] assert result["title"] == datapackage_dict["title"] assert result["version"] == datapackage_dict["version"] - + # TODO: check if the name must be lowercased on frictionless-ckan-mapper #def test_name_is_lowercased(self): # self.datapackage.update( @@ -344,7 +346,7 @@ def test_datapackage_author_as_string(self): author = {"name": "John Smith", "email": "jsmith@email.com"} self.datapackage.update({ 'contributors': [{ - "title": author["name"], + "title": author["name"], "email": author["email"], "role": "author" }] diff --git a/ckanext/datapackager/tests/logic/action/test_create.py b/ckanext/datapackager/tests/logic/action/test_create.py index a0bfd39..820d25a 100644 --- a/ckanext/datapackager/tests/logic/action/test_create.py +++ b/ckanext/datapackager/tests/logic/action/test_create.py @@ -1,11 +1,6 @@ import json import tempfile -from six import StringIO, BytesIO -import six -try: - from unittest import mock -except ImportError: - import mock +from io import StringIO import pytest import responses @@ -48,11 +43,7 @@ def test_it_raises_if_datapackage_is_unsafe(self): ] } - if toolkit.check_ckan_version(min_version="2.9"): - upload = StringIO(json.dumps(datapackage)) - else: - upload = mock.MagicMock() - upload.file = StringIO(json.dumps(datapackage)) + upload = StringIO(json.dumps(datapackage)) with pytest.raises(toolkit.ValidationError): helpers.call_action('package_create_from_datapackage', upload=upload) @@ -294,10 +285,7 @@ def test_it_allows_uploading_a_datapackage(self): } with tempfile.NamedTemporaryFile() as tmpfile: - if six.PY3: - tmpfile.write(six.binary_type(json.dumps(datapackage), 'utf-8')) - else: - tmpfile.write(six.binary_type(json.dumps(datapackage))) + tmpfile.write(bytes(json.dumps(datapackage), 'utf-8')) tmpfile.flush() dataset = helpers.call_action('package_create_from_datapackage', diff --git a/dev-requirements-py2.txt b/dev-requirements-py2.txt deleted file mode 100644 index f9a7e74..0000000 --- a/dev-requirements-py2.txt +++ /dev/null @@ -1,8 +0,0 @@ -responses -requests -pytest==4.6.* -pytest-ckan -pytest-cov -pytest-mock -ckanapi -beautifulsoup4 diff --git a/dev-requirements.txt b/dev-requirements.txt index 879a964..b521abe 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,5 @@ responses requests -pytest==6.0.0 pytest-ckan pytest-cov pytest-mock diff --git a/requirements-py2.txt b/requirements-py2.txt deleted file mode 100644 index e409825..0000000 --- a/requirements-py2.txt +++ /dev/null @@ -1,11 +0,0 @@ -cython -cchardet -datapackage==1.1.3 -frictionless-ckan-mapper>=1.0.7 -jsonschema==2.6.0 -chardet==4.0.0 -python-slugify==1.2.4 -typing-extensions==3.10.0.2 -rfc3986==1.5.0 -six -ckan-datapackage-tools==0.1.0 diff --git a/requirements.txt b/requirements.txt index e547a3a..56fa6da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,4 @@ -cython -cchardet -jsonschema==2.6.0 -python-slugify==1.2.4 -datapackage==1.1.3 +jsonschema==4.23.0 +python-slugify==8.0.4 +datapackage==1.15.4 frictionless-ckan-mapper>=1.0.7 -six diff --git a/setup.py b/setup.py index eae6a94..76b7c13 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,6 @@ # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 2.7', "Programming Language :: Python :: 3", ], keywords='CKAN datapackages FrictionlessData',