From 9ba751e1606e32a1f0d0af22c3d4a30f9f9a0ea2 Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 22:05:43 +0300 Subject: [PATCH 1/9] Deprecated DataPackage.[required_]attributes, improved class readability --- datapackage/datapackage.py | 175 +++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 76 deletions(-) diff --git a/datapackage/datapackage.py b/datapackage/datapackage.py index 81746fa..0456f09 100644 --- a/datapackage/datapackage.py +++ b/datapackage/datapackage.py @@ -15,16 +15,14 @@ import warnings import jsonpointer import datapackage.schema +from .resource import Resource +from .exceptions import DataPackageException from . import config from . import helpers -from .resource import Resource -from .exceptions import ( - DataPackageException, -) class DataPackage(object): - '''Class for loading, validating and working with a Data Package. + """"Class for loading, validating and working with a Data Package. Args: descriptor (dict, str or file-like object, optional): The contents of the @@ -52,7 +50,7 @@ class DataPackage(object): SchemaError: If the :data:`schema` couldn't be loaded or was invalid. RegistryError: If there was some problem loading the :data:`schema` from the registry. - ''' + """ # Public @@ -87,80 +85,48 @@ def __init__(self, descriptor=None, schema='data-package', default_base_path=Non resource['path'] = [path] # Set attributes - self._schema = self._load_schema(schema) - self._resources = self._load_resources(self.descriptor, - self.base_path) + self._schema = datapackage.schema.Schema(schema) + self._resources = self._update_resources((), self.descriptor, self.base_path) def __del__(self): - self._remove_tempdir_if_exists() + if hasattr(self, '_tempdir') and os.path.exists(self._tempdir): + shutil.rmtree(self._tempdir, ignore_errors=True) @property def descriptor(self): - '''dict: The descriptor of this data package. Its attributes can be - changed.''' + """"dict: The descriptor of this data package. Its attributes can be + changed. + """ return self._descriptor @property def schema(self): - ''':class:`.Schema`: This data package's schema. + """:class:`.Schema`: This data package's schema. Check https://github.com/okfn/datapackage-validate-py for documentation on its attributes. - ''' - return self._schema - @property - def base_path(self): - '''str: The base path of this Data Package (can be None).''' - return self._base_path + """ + return self._schema @property def resources(self): - '''The resources defined in this data package (can be empty). + """"The resources defined in this data package (can be empty). To add or remove resources, alter the `resources` attribute of the :data:`descriptor`. :returns: The resources. :rtype: tuple of :class:`.Resource` - ''' + + """ self._resources = self._update_resources(self._resources, self.descriptor, self.base_path) return self._resources - @property - def attributes(self): - '''tuple: The union of the attributes defined in the schema and the - data package (can be empty).''' - attributes = set(self.to_dict().keys()) - try: - attributes.update(self.schema.properties.keys()) - except AttributeError: - pass - return tuple(attributes) - - @property - def required_attributes(self): - '''tuple: The schema's required attributed (can be empty).''' - required = () - try: - if self.schema.required is not None: - required = tuple(self.schema.required) - except AttributeError: - pass - return required - - def to_dict(self): - '''dict: Convert this Data Package to dict.''' - return copy.deepcopy(self.descriptor) - - def to_json(self): - '''str: Convert this Data Package to a JSON string.''' - return json.dumps(self.descriptor) - def save(self, file_or_path): - '''Validates and saves this Data Package contents into a zip file. + """"Validates and saves this Data Package contents into a zip file. It creates a zip file into ``file_or_path`` with the contents of this Data Package and its resources. Every resource which content lives in @@ -199,7 +165,8 @@ def save(self, file_or_path): Raises: ValidationError: If the Data Package is invalid. DataPackageException: If there was some error writing the package. - ''' + + """ self.validate() def arcname(resource): @@ -230,35 +197,30 @@ def arcname(resource): six.raise_from(DataPackageException(e), e) def validate(self): - '''Validate this Data Package. + """"Validate this Data Package. Raises: ValidationError: If the Data Package is invalid. - ''' + + """ descriptor = self.to_dict() self.schema.validate(descriptor) def iter_errors(self): - '''Lazily yields each ValidationError for the received data dict. + """"Lazily yields each ValidationError for the received data dict. Returns: iter: ValidationError for each error in the data. - ''' - return self.schema.iter_errors(self.to_dict()) - - # Deprecated - def safe(self): - warnings.warn( - 'DataPackage.safe is deprecated. Now it\'s always safe.', - UserWarning) - return True + """ + return self.schema.iter_errors(self.to_dict()) # Private def _extract_zip_if_possible(self, descriptor): - '''str: Path to the extracted datapackage.json if descriptor points to - ZIP, or the unaltered descriptor otherwise.''' + """"str: Path to the extracted datapackage.json if descriptor points to + ZIP, or the unaltered descriptor otherwise. + """ result = descriptor try: if isinstance(descriptor, six.string_types): @@ -309,12 +271,6 @@ def _validate_zip(self, the_zip): msg = 'DataPackage must have only one "datapackage.json" (had {n})' raise DataPackageException(msg.format(n=len(datapackage_jsons))) - def _load_schema(self, schema): - return datapackage.schema.Schema(schema) - - def _load_resources(self, descriptor, base_path): - return self._update_resources((), descriptor, base_path) - def _update_resources(self, current_resources, descriptor, base_path): resources_dicts = descriptor.get('resources') new_resources = [] @@ -329,6 +285,73 @@ def _update_resources(self, current_resources, descriptor, base_path): return tuple(new_resources) - def _remove_tempdir_if_exists(self): - if hasattr(self, '_tempdir') and os.path.exists(self._tempdir): - shutil.rmtree(self._tempdir, ignore_errors=True) + # Additional + + @property + def base_path(self): + """"str: The base path of this Data Package (can be None). + """ + return self._base_path + + def to_dict(self): + """"dict: Convert this Data Package to dict. + """ + return copy.deepcopy(self.descriptor) + + def to_json(self): + """"str: Convert this Data Package to a JSON string. + """ + return json.dumps(self.descriptor) + + # Deprecated + + def safe(self): + """True: datapackage is always safe. + """ + + # Deprecate + warnings.warn( + 'DataPackage.safe is deprecated. ' + 'Now it\'s always safe.', + UserWarning) + + return True + + @property + def attributes(self): + """tuple: Attributes defined in the schema and the data package. + """ + + # Deprecate + warnings.warn( + 'DataPackage.attributes is deprecated.', + UserWarning) + + # Get attributes + attributes = set(self.to_dict().keys()) + try: + attributes.update(self.schema.properties.keys()) + except AttributeError: + pass + + return tuple(attributes) + + @property + def required_attributes(self): + """tuple: The schema's required attributed. + """ + + # Deprecate + warnings.warn( + 'DataPackage.attributes_attributes is deprecated.', + UserWarning) + required = () + + # Get required + try: + if self.schema.required is not None: + required = tuple(self.schema.required) + except AttributeError: + pass + + return required From 2ced8faf27c4614dae87e4bac04b263fbeee7767 Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 22:10:22 +0300 Subject: [PATCH 2/9] updated links to Data Package spec in texts --- CONTRIBUTING.md | 4 ++-- README.md | 2 +- setup.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16fdb68..00f6818 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,14 +3,14 @@ Helping out with the development of **datapackage** is much appreciated and we try to be a newcomer friendly project! There's a lot to do if we are supposed to be the goto python package for working with [data -packages](http://www.dataprotocols.org/en/latest/data-packages.html). +packages](http://specs.frictionlessdata.io/data-package/). We don't track an awful lot except for perhaps bugs and feature requests from non-developers (or very busy developers) in our issue tracker so the development is mostly fueled by the *scratch your own itch* mantra. So start off by looking at what [data -packages](http://www.dataprotocols.org/en/latest/data-packages.html) can +packages](http://specs.frictionlessdata.io/data-package/) can do and what feature you would like to see and use. Then just implement it! diff --git a/README.md b/README.md index f843e42..c13a526 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A model for working with [Data Packages]. - [Data Packages]: http://dataprotocols.org/data-packages/ + [Data Packages]: http://specs.frictionlessdata.io/data-package/ ## Install diff --git a/setup.py b/setup.py index 3fe17a5..b5f67b1 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ def recursive_glob(path, patterns): name='datapackage', version='1.0.0-alpha1', description=( - 'Utilities to work with Data Packages as defined on dataprotocols.org' + 'Utilities to work with Data Packages as defined on specs.frictionlessdata.io' ), long_description=long_description, @@ -62,7 +62,7 @@ def recursive_glob(path, patterns): 'Programming Language :: Python :: 3.5', ], - keywords='data dataprotocols jsontableschema frictionlessdata datascience', + keywords='data jsontableschema frictionlessdata datascience', packages=find_packages(exclude=['tests']), package_data={'datapackage': schema_files()}, From e1821a96ada71cd3151342294f0d8c722916eaf6 Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 22:21:37 +0300 Subject: [PATCH 3/9] merged pushpull and mappers (one responsibility) --- datapackage/mappers.py | 102 -------------------------------------- datapackage/pushpull.py | 105 +++++++++++++++++++++++++++++++++++++--- tests/test_mappers.py | 50 ------------------- tests/test_pushpull.py | 39 +++++++++++++++ 4 files changed, 138 insertions(+), 158 deletions(-) delete mode 100644 datapackage/mappers.py delete mode 100644 tests/test_mappers.py diff --git a/datapackage/mappers.py b/datapackage/mappers.py deleted file mode 100644 index 8aeb86b..0000000 --- a/datapackage/mappers.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import -from __future__ import unicode_literals - -import os -import re -from copy import deepcopy - - -# Module API - -def convert_path(path, name): - """Convert resource's path and name to storage's table name. - - Args: - path (str): resource path - name (str): resource name - - Returns: - str: table name - - """ - table = os.path.splitext(path)[0] - table = table.replace(os.path.sep, '__') - if name is not None: - table = '___'.join([table, name]) - table = re.sub('[^0-9a-zA-Z_]+', '_', table) - table = table.lower() - return table - - -def restore_path(table): - """Restore resource's path and name from storage's table. - - Args: - table (str): table name - - Returns: - (str, str): resource path and name - - """ - name = None - splited = table.split('___') - path = splited[0] - if len(splited) == 2: - name = splited[1] - path = path.replace('__', os.path.sep) - path += '.csv' - return path, name - - -def convert_schemas(mapping, schemas): - """Convert schemas to be compatible with storage schemas. - - Foreign keys related operations. - - Args: - mapping (dict): mapping between resource name and table name - schemas (list): schemas - - Raises: - ValueError: if there is no resource - for some foreign key in given mapping - - Returns: - list: converted schemas - - """ - schemas = deepcopy(schemas) - for schema in schemas: - for fk in schema.get('foreignKeys', []): - resource = fk['reference']['resource'] - if resource != 'self': - if resource not in mapping: - message = 'Not resource "%s" for foreign key "%s"' - message = message % (resource, fk) - raise ValueError(message) - fk['reference']['resource'] = mapping[resource] - return schemas - - -def restore_resources(resources): - """Restore schemas from being compatible with storage schemas. - - Foreign keys related operations. - - Args: - list: resources from storage - - Returns: - list: restored resources - - """ - resources = deepcopy(resources) - for resource in resources: - schema = resource['schema'] - for fk in schema.get('foreignKeys', []): - _, name = restore_path(fk['reference']['resource']) - fk['reference']['resource'] = name - return resources diff --git a/datapackage/pushpull.py b/datapackage/pushpull.py index 160773d..c476d1c 100644 --- a/datapackage/pushpull.py +++ b/datapackage/pushpull.py @@ -6,15 +6,15 @@ import io import os +import re import six import json import unicodecsv as csv from copy import deepcopy from importlib import import_module from jsontableschema import Schema -from . import helpers -from . import mappers from .datapackage import DataPackage +from . import helpers # Module API @@ -47,7 +47,7 @@ def push_datapackage(descriptor, backend, **backend_options): # Collect tables/schemas/data for resource in model.resources: name = resource.descriptor.get('name', None) - table = mappers.convert_path(resource.descriptor['path'][0], name) + table = _convert_path(resource.descriptor['path'][0], name) schema = resource.descriptor['schema'] data = resource.table.iter(keyed=True) # TODO: review @@ -62,7 +62,7 @@ def values(schema, data): datamap[table] = values(schema, data) if name is not None: mapping[name] = table - schemas = mappers.convert_schemas(mapping, schemas) + schemas = _convert_schemas(mapping, schemas) # Create tables for table in tables: @@ -104,7 +104,7 @@ def pull_datapackage(descriptor, name, backend, **backend_options): # Prepare schema = storage.describe(table) base = os.path.dirname(descriptor) - path, name = mappers.restore_path(table) + path, name = _restore_path(table) fullpath = os.path.join(base, path) # Write data @@ -129,7 +129,7 @@ def pull_datapackage(descriptor, name, backend, **backend_options): if six.PY2: mode = 'wb' encoding = None - resources = mappers.restore_resources(resources) + resources = _restore_resources(resources) helpers.ensure_dir(descriptor) with io.open(descriptor, mode=mode, @@ -140,3 +140,96 @@ def pull_datapackage(descriptor, name, backend, **backend_options): } json.dump(descriptor, file, indent=4) return storage + + +# Internal + +def _convert_path(path, name): + """Convert resource's path and name to storage's table name. + + Args: + path (str): resource path + name (str): resource name + + Returns: + str: table name + + """ + table = os.path.splitext(path)[0] + table = table.replace(os.path.sep, '__') + if name is not None: + table = '___'.join([table, name]) + table = re.sub('[^0-9a-zA-Z_]+', '_', table) + table = table.lower() + return table + + +def _restore_path(table): + """Restore resource's path and name from storage's table. + + Args: + table (str): table name + + Returns: + (str, str): resource path and name + + """ + name = None + splited = table.split('___') + path = splited[0] + if len(splited) == 2: + name = splited[1] + path = path.replace('__', os.path.sep) + path += '.csv' + return path, name + + +def _convert_schemas(mapping, schemas): + """Convert schemas to be compatible with storage schemas. + + Foreign keys related operations. + + Args: + mapping (dict): mapping between resource name and table name + schemas (list): schemas + + Raises: + ValueError: if there is no resource + for some foreign key in given mapping + + Returns: + list: converted schemas + + """ + schemas = deepcopy(schemas) + for schema in schemas: + for fk in schema.get('foreignKeys', []): + resource = fk['reference']['resource'] + if resource != 'self': + if resource not in mapping: + message = 'Not resource "%s" for foreign key "%s"' + message = message % (resource, fk) + raise ValueError(message) + fk['reference']['resource'] = mapping[resource] + return schemas + + +def _restore_resources(resources): + """Restore schemas from being compatible with storage schemas. + + Foreign keys related operations. + + Args: + list: resources from storage + + Returns: + list: restored resources + + """ + resources = deepcopy(resources) + for resource in resources: + schema = resource['schema'] + for fk in schema.get('foreignKeys', []): + _, name = _restore_path(fk['reference']['resource']) + fk['reference']['resource'] = name + return resources diff --git a/tests/test_mappers.py b/tests/test_mappers.py deleted file mode 100644 index 5e85ae0..0000000 --- a/tests/test_mappers.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import -from __future__ import unicode_literals - -import pytest -from importlib import import_module -module = import_module('datapackage.mappers') - - -# Tests - -def test_convert_path(): - assert module.convert_path('path.csv', 'name') == 'path___name' - assert module.convert_path('dir/path.csv', 'name') == 'dir__path___name' - assert module.convert_path('path.csv', 'Some Name') == 'path___some_name' - - -def test_restore_path(): - assert module.restore_path('path___name') == ('path.csv', 'name') - assert module.restore_path('dir__path___name') == ('dir/path.csv', 'name') - assert module.restore_path('path___some_name') == ('path.csv', 'some_name') - - -def test_convert_schemas_self_reference(): - mapping = {} - schema = {'foreignKeys': [{'reference': {'resource': 'self'}}]} - result = module.convert_schemas(mapping, [schema]) - assert result[0] == schema - - -def test_convert_schemas_resource_reference(): - mapping = {'resource_name': 'table_name'} - schema = {'foreignKeys': [{'reference': {'resource': 'resource_name'}}]} - result = module.convert_schemas(mapping, [schema]) - assert result[0] == {'foreignKeys': [{'reference': {'resource': 'table_name'}}]} - - -def test_convert_schemas_resource_missing(): - mapping = {} - schema = {'foreignKeys': [{'reference': {'resource': 'resource_name'}}]} - with pytest.raises(ValueError): - module.convert_schemas(mapping, [schema]) - - -def test_restore_resources(): - resource = {'schema': {'foreignKeys': [{'reference': {'resource': 'path___name'}}]}} - result = module.restore_resources([resource]) - assert result[0] == {'schema': {'foreignKeys': [{'reference': {'resource': 'name'}}]}} diff --git a/tests/test_pushpull.py b/tests/test_pushpull.py index 39d4a49..1cb1858 100644 --- a/tests/test_pushpull.py +++ b/tests/test_pushpull.py @@ -67,6 +67,45 @@ def test_pull_datapackage(m1, m2, storage, descriptor): {'name': 'city', 'type': 'string'}]}}]} +def test_convert_path(): + assert module._convert_path('path.csv', 'name') == 'path___name' + assert module._convert_path('dir/path.csv', 'name') == 'dir__path___name' + assert module._convert_path('path.csv', 'Some Name') == 'path___some_name' + + +def test_restore_path(): + assert module._restore_path('path___name') == ('path.csv', 'name') + assert module._restore_path('dir__path___name') == ('dir/path.csv', 'name') + assert module._restore_path('path___some_name') == ('path.csv', 'some_name') + + +def test_convert_schemas_self_reference(): + mapping = {} + schema = {'foreignKeys': [{'reference': {'resource': 'self'}}]} + result = module._convert_schemas(mapping, [schema]) + assert result[0] == schema + + +def test_convert_schemas_resource_reference(): + mapping = {'resource_name': 'table_name'} + schema = {'foreignKeys': [{'reference': {'resource': 'resource_name'}}]} + result = module._convert_schemas(mapping, [schema]) + assert result[0] == {'foreignKeys': [{'reference': {'resource': 'table_name'}}]} + + +def test_convert_schemas_resource_missing(): + mapping = {} + schema = {'foreignKeys': [{'reference': {'resource': 'resource_name'}}]} + with pytest.raises(ValueError): + module._convert_schemas(mapping, [schema]) + + +def test_restore_resources(): + resource = {'schema': {'foreignKeys': [{'reference': {'resource': 'path___name'}}]}} + result = module._restore_resources([resource]) + assert result[0] == {'schema': {'foreignKeys': [{'reference': {'resource': 'name'}}]}} + + # Fixtures @pytest.fixture From b58c4fcb75a7e302570af471b0511e0467db86c2 Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 22:24:30 +0300 Subject: [PATCH 4/9] minor import change in datapackage file --- datapackage/datapackage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/datapackage/datapackage.py b/datapackage/datapackage.py index 0456f09..cf1353b 100644 --- a/datapackage/datapackage.py +++ b/datapackage/datapackage.py @@ -16,9 +16,9 @@ import jsonpointer import datapackage.schema from .resource import Resource -from .exceptions import DataPackageException -from . import config +from . import exceptions from . import helpers +from . import config class DataPackage(object): @@ -194,7 +194,7 @@ def arcname(resource): except (IOError, zipfile.BadZipfile, zipfile.LargeZipFile) as e: - six.raise_from(DataPackageException(e), e) + six.raise_from(exceptions.DataPackageException(e), e) def validate(self): """"Validate this Data Package. @@ -269,7 +269,7 @@ def _validate_zip(self, the_zip): if f.endswith('datapackage.json')] if len(datapackage_jsons) != 1: msg = 'DataPackage must have only one "datapackage.json" (had {n})' - raise DataPackageException(msg.format(n=len(datapackage_jsons))) + raise exceptions.DataPackageException(msg.format(n=len(datapackage_jsons))) def _update_resources(self, current_resources, descriptor, base_path): resources_dicts = descriptor.get('resources') From 8237376c0383d1941f42fbd1d302da746f2f640f Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 23:48:38 +0300 Subject: [PATCH 5/9] deprecated DataPackage.schema --- datapackage/datapackage.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/datapackage/datapackage.py b/datapackage/datapackage.py index cf1353b..859a1ee 100644 --- a/datapackage/datapackage.py +++ b/datapackage/datapackage.py @@ -99,16 +99,6 @@ def descriptor(self): """ return self._descriptor - @property - def schema(self): - """:class:`.Schema`: This data package's schema. - - Check https://github.com/okfn/datapackage-validate-py for documentation - on its attributes. - - """ - return self._schema - @property def resources(self): """"The resources defined in this data package (can be empty). @@ -355,3 +345,16 @@ def required_attributes(self): pass return required + + @property + def schema(self): + """:class:`.Schema`: This data package's schema. + """ + + # Deprecate + warnings.warn( + 'DataPackage.schema is deprecated.', + UserWarning) + required = () + + return self._schema From 2850cefa87d4fee471e5f1c112909f41b2a1b125 Mon Sep 17 00:00:00 2001 From: roll Date: Thu, 13 Apr 2017 23:52:38 +0300 Subject: [PATCH 6/9] moved jsonschemas from specs forlder to profiles folder --- Makefile | 8 ++--- .../{specs => profiles}/data-package.json | 0 .../fiscal-data-package.json | 0 datapackage/{specs => profiles}/registry.json | 0 .../tabular-data-package.json | 0 datapackage/registry.py | 2 +- tests/test_profiles.py | 36 +++++++++++++++++++ tests/test_specs.py | 36 ------------------- 8 files changed, 41 insertions(+), 41 deletions(-) rename datapackage/{specs => profiles}/data-package.json (100%) rename datapackage/{specs => profiles}/fiscal-data-package.json (100%) rename datapackage/{specs => profiles}/registry.json (100%) rename datapackage/{specs => profiles}/tabular-data-package.json (100%) create mode 100644 tests/test_profiles.py delete mode 100644 tests/test_specs.py diff --git a/Makefile b/Makefile index de3711e..f8bde8d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ specs: - wget -O datapackage/specs/registry.json https://specs.frictionlessdata.io/schemas/registry.json - wget -O datapackage/specs/data-package.json https://specs.frictionlessdata.io/schemas/data-package.json - wget -O datapackage/specs/tabular-data-package.json https://specs.frictionlessdata.io/schemas/tabular-data-package.json - wget -O datapackage/specs/fiscal-data-package.json https://specs.frictionlessdata.io/schemas/fiscal-data-package.json + wget -O datapackage/profiles/registry.json https://specs.frictionlessdata.io/schemas/registry.json + wget -O datapackage/profiles/data-package.json https://specs.frictionlessdata.io/schemas/data-package.json + wget -O datapackage/profiles/tabular-data-package.json https://specs.frictionlessdata.io/schemas/tabular-data-package.json + wget -O datapackage/profiles/fiscal-data-package.json https://specs.frictionlessdata.io/schemas/fiscal-data-package.json diff --git a/datapackage/specs/data-package.json b/datapackage/profiles/data-package.json similarity index 100% rename from datapackage/specs/data-package.json rename to datapackage/profiles/data-package.json diff --git a/datapackage/specs/fiscal-data-package.json b/datapackage/profiles/fiscal-data-package.json similarity index 100% rename from datapackage/specs/fiscal-data-package.json rename to datapackage/profiles/fiscal-data-package.json diff --git a/datapackage/specs/registry.json b/datapackage/profiles/registry.json similarity index 100% rename from datapackage/specs/registry.json rename to datapackage/profiles/registry.json diff --git a/datapackage/specs/tabular-data-package.json b/datapackage/profiles/tabular-data-package.json similarity index 100% rename from datapackage/specs/tabular-data-package.json rename to datapackage/profiles/tabular-data-package.json diff --git a/datapackage/registry.py b/datapackage/registry.py index 573fbd2..75bced8 100644 --- a/datapackage/registry.py +++ b/datapackage/registry.py @@ -27,7 +27,7 @@ class Registry(object): DEFAULT_REGISTRY_URL = 'https://specs.frictionlessdata.io/specs/registry.json' DEFAULT_REGISTRY_PATH = os.path.join( os.path.dirname(os.path.abspath(__file__)), - 'specs', + 'profiles', 'registry.json' ) diff --git a/tests/test_profiles.py b/tests/test_profiles.py new file mode 100644 index 0000000..844906c --- /dev/null +++ b/tests/test_profiles.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import division +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals + +import io +import json +import pytest +import requests + + +# Tests + +def test_profiles_registry_is_up_to_date(): + local = io.open('datapackage/profiles/registry.json').read() + remote = requests.get('https://specs.frictionlessdata.io/schemas/registry.json').text + assert local == remote, 'run `make profiles` to update profiles' + + +def test_profiles_data_package_is_up_to_date(): + local = io.open('datapackage/profiles/data-package.json').read() + remote = requests.get('https://specs.frictionlessdata.io/schemas/data-package.json').text + assert local == remote, 'run `make profiles` to update profiles' + + +def test_profiles_tabular_data_package_is_up_to_date(): + local = io.open('datapackage/profiles/tabular-data-package.json').read() + remote = requests.get('https://specs.frictionlessdata.io/schemas/tabular-data-package.json').text + assert local == remote, 'run `make profiles` to update profiles' + + +def test_profiles_fiscal_data_package_is_up_to_date(): + local = io.open('datapackage/profiles/fiscal-data-package.json').read() + remote = requests.get('https://specs.frictionlessdata.io/schemas/fiscal-data-package.json').text + assert local == remote, 'run `make profiles` to update profiles' diff --git a/tests/test_specs.py b/tests/test_specs.py deleted file mode 100644 index 3cdc857..0000000 --- a/tests/test_specs.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import -from __future__ import unicode_literals - -import io -import json -import pytest -import requests - - -# Tests - -def test_specs_registry_is_up_to_date(): - local = io.open('datapackage/specs/registry.json').read() - remote = requests.get('https://specs.frictionlessdata.io/schemas/registry.json').text - assert local == remote, 'run `make specs` to update specs' - - -def test_specs_data_package_is_up_to_date(): - local = io.open('datapackage/specs/data-package.json').read() - remote = requests.get('https://specs.frictionlessdata.io/schemas/data-package.json').text - assert local == remote, 'run `make specs` to update specs' - - -def test_specs_tabular_data_package_is_up_to_date(): - local = io.open('datapackage/specs/tabular-data-package.json').read() - remote = requests.get('https://specs.frictionlessdata.io/schemas/tabular-data-package.json').text - assert local == remote, 'run `make specs` to update specs' - - -def test_specs_fiscal_data_package_is_up_to_date(): - local = io.open('datapackage/specs/fiscal-data-package.json').read() - remote = requests.get('https://specs.frictionlessdata.io/schemas/fiscal-data-package.json').text - assert local == remote, 'run `make specs` to update specs' From b1cefc22b32e4a812f286fde0b977b4f27b4072d Mon Sep 17 00:00:00 2001 From: roll Date: Fri, 14 Apr 2017 08:15:29 +0300 Subject: [PATCH 7/9] Added Resource.name --- datapackage/resource.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datapackage/resource.py b/datapackage/resource.py index 1365dd6..edd0833 100644 --- a/datapackage/resource.py +++ b/datapackage/resource.py @@ -66,6 +66,12 @@ def descriptor(self): """ return self.__descriptor + @property + def name(self): + """dict: resource name + """ + return self.__descriptor['name'] + @property def source_type(self): """str: data source type From 65b0351d7d31f2aac59d1f24d0ead5c4aee240e5 Mon Sep 17 00:00:00 2001 From: roll Date: Fri, 14 Apr 2017 08:51:45 +0300 Subject: [PATCH 8/9] deprecated DataPackage.schema and schema argument (use descriptor.profile) --- datapackage/datapackage.py | 54 +++++++++++++++++++++++++++----------- tests/test_datapackage.py | 33 +++++++++-------------- tests/test_pushpull.py | 23 +++++++++------- 3 files changed, 65 insertions(+), 45 deletions(-) diff --git a/datapackage/datapackage.py b/datapackage/datapackage.py index 859a1ee..c474c2b 100644 --- a/datapackage/datapackage.py +++ b/datapackage/datapackage.py @@ -54,7 +54,7 @@ class DataPackage(object): # Public - def __init__(self, descriptor=None, schema='data-package', default_base_path=None): + def __init__(self, descriptor=None, schema=None, default_base_path=None): # Extract from zip descriptor = self._extract_zip_if_possible(descriptor) @@ -67,6 +67,24 @@ def __init__(self, descriptor=None, schema='data-package', default_base_path=Non helpers.dereference_data_package_descriptor(self._descriptor, self._base_path) helpers.expand_data_package_descriptor(self._descriptor) + # Get profile + profile = self._descriptor['profile'] + + # Handle deprecated schema argument + if schema is not None: + warnings.warn( + 'Argument "schema" is deprecated. ' + 'Please use "descriptor.profile" property.', + UserWarning) + if isinstance(schema, six.string_types): + if schema in ['base', 'default']: + schema = 'data-package' + elif schema == 'tabular': + schema = 'tabular-data-package' + elif schema == 'fiscal': + schema = 'fiscal-data-package' + profile = schema + # Handle deprecated resource.path/url for resource in self._descriptor.get('resources', []): url = resource.pop('url', None) @@ -85,7 +103,7 @@ def __init__(self, descriptor=None, schema='data-package', default_base_path=Non resource['path'] = [path] # Set attributes - self._schema = datapackage.schema.Schema(schema) + self._schema = datapackage.schema.Schema(profile) self._resources = self._update_resources((), self.descriptor, self.base_path) def __del__(self): @@ -99,6 +117,12 @@ def descriptor(self): """ return self._descriptor + @property + def profile(self): + """"str: The profile of this data package. + """ + return self._descriptor['profile'] + @property def resources(self): """"The resources defined in this data package (can be empty). @@ -307,6 +331,19 @@ def safe(self): return True + @property + def schema(self): + """:class:`.Schema`: This data package's schema. + """ + + # Deprecate + warnings.warn( + 'DataPackage.schema is deprecated.', + UserWarning) + required = () + + return self._schema + @property def attributes(self): """tuple: Attributes defined in the schema and the data package. @@ -345,16 +382,3 @@ def required_attributes(self): pass return required - - @property - def schema(self): - """:class:`.Schema`: This data package's schema. - """ - - # Deprecate - warnings.warn( - 'DataPackage.schema is deprecated.', - UserWarning) - required = () - - return self._schema diff --git a/tests/test_datapackage.py b/tests/test_datapackage.py index 79118c6..baa76c7 100644 --- a/tests/test_datapackage.py +++ b/tests/test_datapackage.py @@ -16,6 +16,7 @@ import httpretty import tests.test_helpers as test_helpers import datapackage +from datapackage import helpers # Tests @@ -257,18 +258,14 @@ def test_to_json(self): dp = datapackage.DataPackage(descriptor) assert json.loads(dp.to_json()) == descriptor - @mock.patch('datapackage.helpers.expand_resource_descriptor') - @mock.patch('datapackage.helpers.expand_data_package_descriptor') - def test_descriptor_dereferencing_uri(self, m1, m2): + def test_descriptor_dereferencing_uri(self): dp = datapackage.DataPackage('tests/fixtures/datapackage_with_dereferencing.json') - assert dp.descriptor['resources'] == [ + assert dp.descriptor['resources'] == list(map(helpers.expand_resource_descriptor, [ {'name': 'name1', 'data': 'data', 'schema': {'fields': [{'name': 'name'}]}}, {'name': 'name2', 'data': 'data', 'dialect': {'delimiter': ','}}, - ] + ])) - @mock.patch('datapackage.helpers.expand_resource_descriptor') - @mock.patch('datapackage.helpers.expand_data_package_descriptor') - def test_descriptor_dereferencing_uri_pointer(self, m1, m2): + def test_descriptor_dereferencing_uri_pointer(self): descriptor = { 'resources': [ {'name': 'name1', 'data': 'data', 'schema': '#/schemas/main'}, @@ -278,10 +275,10 @@ def test_descriptor_dereferencing_uri_pointer(self, m1, m2): 'dialects': [{'delimiter': ','}], } dp = datapackage.DataPackage(descriptor) - assert dp.descriptor['resources'] == [ + assert dp.descriptor['resources'] == list(map(helpers.expand_resource_descriptor, [ {'name': 'name1', 'data': 'data', 'schema': {'fields': [{'name': 'name'}]}}, {'name': 'name2', 'data': 'data', 'dialect': {'delimiter': ','}}, - ] + ])) def test_descriptor_dereferencing_uri_pointer_bad(self): descriptor = { @@ -293,9 +290,7 @@ def test_descriptor_dereferencing_uri_pointer_bad(self): dp = datapackage.DataPackage(descriptor) @httpretty.activate - @mock.patch('datapackage.helpers.expand_resource_descriptor') - @mock.patch('datapackage.helpers.expand_data_package_descriptor') - def test_descriptor_dereferencing_uri_remote(self, m1, m2): + def test_descriptor_dereferencing_uri_remote(self): # Mocks httpretty.register_uri(httpretty.GET, 'http://example.com/schema', body='{"fields": [{"name": "name"}]}') @@ -309,10 +304,10 @@ def test_descriptor_dereferencing_uri_remote(self, m1, m2): ], } dp = datapackage.DataPackage(descriptor) - assert dp.descriptor['resources'] == [ + assert dp.descriptor['resources'] == list(map(helpers.expand_resource_descriptor, [ {'name': 'name1', 'data': 'data', 'schema': {'fields': [{'name': 'name'}]}}, {'name': 'name2', 'data': 'data', 'dialect': {'delimiter': ','}}, - ] + ])) def test_descriptor_dereferencing_uri_remote_bad(self): # Mocks @@ -326,9 +321,7 @@ def test_descriptor_dereferencing_uri_remote_bad(self): with pytest.raises(datapackage.exceptions.DataPackageException): dp = datapackage.DataPackage(descriptor) - @mock.patch('datapackage.helpers.expand_resource_descriptor') - @mock.patch('datapackage.helpers.expand_data_package_descriptor') - def test_descriptor_dereferencing_uri_local(self, m1, m2): + def test_descriptor_dereferencing_uri_local(self): descriptor = { 'resources': [ {'name': 'name1', 'data': 'data', 'schema': 'table_schema.json'}, @@ -336,10 +329,10 @@ def test_descriptor_dereferencing_uri_local(self, m1, m2): ], } dp = datapackage.DataPackage(descriptor, default_base_path='tests/fixtures') - assert dp.descriptor['resources'] == [ + assert dp.descriptor['resources'] == list(map(helpers.expand_resource_descriptor, [ {'name': 'name1', 'data': 'data', 'schema': {'fields': [{'name': 'name'}]}}, {'name': 'name2', 'data': 'data', 'dialect': {'delimiter': ','}}, - ] + ])) def test_descriptor_dereferencing_uri_local_bad(self): descriptor = { diff --git a/tests/test_pushpull.py b/tests/test_pushpull.py index 1cb1858..7071770 100644 --- a/tests/test_pushpull.py +++ b/tests/test_pushpull.py @@ -9,10 +9,11 @@ import pytest import shutil import tempfile -import tests.test_helpers as helpers from mock import patch, ANY from datapackage import DataPackage +from datapackage import helpers from importlib import import_module +from tests import test_helpers module = import_module('datapackage.pushpull') @@ -21,7 +22,7 @@ def test_push_datapackage(storage): # Prepare and call - descriptor = helpers.fixture_path('datapackage', 'datapackage.json') + descriptor = test_helpers.fixture_path('datapackage', 'datapackage.json') storage.buckets = ['data___data'] # Without patch it's a reflection module.push_datapackage(descriptor=descriptor, backend='backend') @@ -42,9 +43,7 @@ def test_push_datapackage(storage): ] -@mock.patch('datapackage.helpers.expand_resource_descriptor') -@mock.patch('datapackage.helpers.expand_data_package_descriptor') -def test_pull_datapackage(m1, m2, storage, descriptor): +def test_pull_datapackage(storage, descriptor): # Prepare and call storage.buckets = ['data___data'] @@ -60,11 +59,15 @@ def test_pull_datapackage(m1, m2, storage, descriptor): # Assert pulled datapackage dp = DataPackage(descriptor) - assert dp.descriptor == {'name': 'name', 'resources': [ - {'path': ['data.csv'], 'name': 'data', 'schema': - {'fields': [ - {'name': 'id', 'type': 'integer'}, - {'name': 'city', 'type': 'string'}]}}]} + assert dp.descriptor == helpers.expand_data_package_descriptor( + {'name': 'name', + 'resources': [ + {'path': ['data.csv'], + 'name': 'data', + 'schema': + {'fields': [ + {'name': 'id', 'type': 'integer'}, + {'name': 'city', 'type': 'string'}]}}]}) def test_convert_path(): From 7c310db34313416bcccdd2740829a8565b7c7a02 Mon Sep 17 00:00:00 2001 From: roll Date: Fri, 14 Apr 2017 09:16:18 +0300 Subject: [PATCH 9/9] renamed Schema to Profile, introduced DataPackage.profile --- datapackage/__init__.py | 13 +++++- datapackage/datapackage.py | 54 ++++++++++----------- datapackage/{schema.py => profile.py} | 57 ++++++++++++++++------- tests/test_datapackage.py | 8 ++-- tests/{test_schema.py => test_profile.py} | 52 ++++++++++----------- 5 files changed, 109 insertions(+), 75 deletions(-) rename datapackage/{schema.py => profile.py} (80%) rename tests/{test_schema.py => test_profile.py} (84%) diff --git a/datapackage/__init__.py b/datapackage/__init__.py index 1cc24e9..1a2a44f 100644 --- a/datapackage/__init__.py +++ b/datapackage/__init__.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +# Module API + from .datapackage import DataPackage -from .pushpull import push_datapackage, pull_datapackage from .resource import Resource +from .profile import Profile + +# Deprecated +from .pushpull import push_datapackage, pull_datapackage diff --git a/datapackage/datapackage.py b/datapackage/datapackage.py index c474c2b..5221d61 100644 --- a/datapackage/datapackage.py +++ b/datapackage/datapackage.py @@ -14,8 +14,8 @@ import requests import warnings import jsonpointer -import datapackage.schema from .resource import Resource +from .profile import Profile from . import exceptions from . import helpers from . import config @@ -103,7 +103,7 @@ def __init__(self, descriptor=None, schema=None, default_base_path=None): resource['path'] = [path] # Set attributes - self._schema = datapackage.schema.Schema(profile) + self._profile = Profile(profile) self._resources = self._update_resources((), self.descriptor, self.base_path) def __del__(self): @@ -121,7 +121,7 @@ def descriptor(self): def profile(self): """"str: The profile of this data package. """ - return self._descriptor['profile'] + return self._profile @property def resources(self): @@ -218,7 +218,7 @@ def validate(self): """ descriptor = self.to_dict() - self.schema.validate(descriptor) + self.profile.validate(descriptor) def iter_errors(self): """"Lazily yields each ValidationError for the received data dict. @@ -227,7 +227,25 @@ def iter_errors(self): iter: ValidationError for each error in the data. """ - return self.schema.iter_errors(self.to_dict()) + return self.profile.iter_errors(self.to_dict()) + + # Additional + + @property + def base_path(self): + """"str: The base path of this Data Package (can be None). + """ + return self._base_path + + def to_dict(self): + """"dict: Convert this Data Package to dict. + """ + return copy.deepcopy(self.descriptor) + + def to_json(self): + """"str: Convert this Data Package to a JSON string. + """ + return json.dumps(self.descriptor) # Private @@ -299,24 +317,6 @@ def _update_resources(self, current_resources, descriptor, base_path): return tuple(new_resources) - # Additional - - @property - def base_path(self): - """"str: The base path of this Data Package (can be None). - """ - return self._base_path - - def to_dict(self): - """"dict: Convert this Data Package to dict. - """ - return copy.deepcopy(self.descriptor) - - def to_json(self): - """"str: Convert this Data Package to a JSON string. - """ - return json.dumps(self.descriptor) - # Deprecated def safe(self): @@ -342,7 +342,7 @@ def schema(self): UserWarning) required = () - return self._schema + return self._profile @property def attributes(self): @@ -357,7 +357,7 @@ def attributes(self): # Get attributes attributes = set(self.to_dict().keys()) try: - attributes.update(self.schema.properties.keys()) + attributes.update(self.profile.properties.keys()) except AttributeError: pass @@ -376,8 +376,8 @@ def required_attributes(self): # Get required try: - if self.schema.required is not None: - required = tuple(self.schema.required) + if self.profile.required is not None: + required = tuple(self.profile.required) except AttributeError: pass diff --git a/datapackage/schema.py b/datapackage/profile.py similarity index 80% rename from datapackage/schema.py rename to datapackage/profile.py index d005999..5184a53 100644 --- a/datapackage/schema.py +++ b/datapackage/profile.py @@ -10,17 +10,16 @@ import json import jsonschema import datapackage.registry -from .exceptions import ( - SchemaError, - ValidationError, -) +from .exceptions import SchemaError, ValidationError -class Schema(object): - '''Abstracts a JSON Schema and allows validation of data against it. +# Module API + +class Profile(object): + """Abstracts a JSON Schema and allows validation of data against it. Args: - schema (str or dict): The JSON Schema itself as a dict, a local path + profile (str or dict): The JSON Schema itself as a dict, a local path or URL to it. Raises: @@ -30,43 +29,67 @@ class Schema(object): Warning: The schema objects created with this class are read-only. You should change any of its attributes after creation. - ''' - def __init__(self, schema): + + """ + + # Public + + def __init__(self, profile): + self._name = profile self._registry = self._load_registry() - self._schema = self._load_schema(schema, self._registry) + self._schema = self._load_schema(profile, self._registry) self._validator = self._load_validator(self._schema, self._registry) self._check_schema() - def to_dict(self): - '''dict: Convert this :class:`.Schema` to dict.''' - return copy.deepcopy(self._schema) + @property + def name(self): + """str: profile name, path or URL. + """ + return self._name + + @property + def jsonschema(self): + """dict: profile jsonschema. + """ + return self._schema def validate(self, data): - '''Validates a data dict against this schema. + """Validates a data dict against this schema. Args: data (dict): The data to be validated. Raises: ValidationError: If the data is invalid. - ''' + + """ try: self._validator.validate(data) except jsonschema.ValidationError as e: six.raise_from(ValidationError.create_from(e), e) def iter_errors(self, data): - '''Lazily yields each ValidationError for the received data dict. + """Lazily yields each ValidationError for the received data dict. Args: data (dict): The data to be validated. Returns: iter: ValidationError for each error in the data. - ''' + + """ for error in self._validator.iter_errors(data): yield ValidationError.create_from(error) + # Additional + + def to_dict(self): + """dict: Convert this :class:`.Schema` to dict. + """ + return copy.deepcopy(self._schema) + + # Private + def _load_registry(self): return datapackage.registry.Registry() diff --git a/tests/test_datapackage.py b/tests/test_datapackage.py index baa76c7..e4ecba2 100644 --- a/tests/test_datapackage.py +++ b/tests/test_datapackage.py @@ -216,13 +216,13 @@ def test_validate_raises_validation_error_if_invalid(self): with pytest.raises(datapackage.exceptions.ValidationError): dp.validate() - @mock.patch('datapackage.schema.Schema') - def test_iter_errors_returns_schemas_iter_errors(self, schema_mock): + @mock.patch('datapackage.datapackage.Profile') + def test_iter_errors_returns_schemas_iter_errors(self, profile_mock): iter_errors_mock = mock.Mock() iter_errors_mock.return_value = 'the iter errors' - schema_mock.return_value.iter_errors = iter_errors_mock + profile_mock.return_value.iter_errors = iter_errors_mock - descriptor = {} + descriptor = {'resources': [{'name': 'name', 'data': ['data']}]} dp = datapackage.DataPackage(descriptor) assert dp.iter_errors() == 'the iter errors' diff --git a/tests/test_schema.py b/tests/test_profile.py similarity index 84% rename from tests/test_schema.py rename to tests/test_profile.py index 6a95c3d..85df3ce 100644 --- a/tests/test_schema.py +++ b/tests/test_profile.py @@ -4,15 +4,15 @@ import tests.test_helpers as test_helpers import datapackage.exceptions -from datapackage.schema import Schema +from datapackage.profile import Profile -class TestSchema(object): +class TestProfile(object): def test_init_loads_schema_from_dict(self): schema_dict = { 'foo': 'bar' } - schema = Schema(schema_dict) + schema = Profile(schema_dict) assert schema.to_dict().keys() == schema_dict.keys() assert schema.to_dict()['foo'] == schema_dict['foo'] @@ -21,23 +21,23 @@ def test_init_changing_the_original_schema_dict_doesnt_change_schema(self): schema_dict = { 'foo': 'bar' } - schema = Schema(schema_dict) + schema = Profile(schema_dict) schema_dict['bar'] = 'baz' assert 'bar' not in schema.to_dict() def test_init_loads_schema_from_path(self): schema_path = test_helpers.fixture_path('empty_schema.json') - assert Schema(schema_path).to_dict() == {} + assert Profile(schema_path).to_dict() == {} def test_init_raises_if_path_doesnt_exist(self): with pytest.raises(datapackage.exceptions.SchemaError): - Schema('inexistent_schema.json') + Profile('inexistent_schema.json') def test_init_raises_if_path_isnt_a_json(self): not_a_json_path = test_helpers.fixture_path('not_a_json') with pytest.raises(datapackage.exceptions.SchemaError): - Schema(not_a_json_path) + Profile(not_a_json_path) @httpretty.activate def test_init_loads_schema_from_url(self): @@ -49,7 +49,7 @@ def test_init_loads_schema_from_url(self): httpretty.register_uri(httpretty.GET, url, body=body, content_type='application/json') - assert Schema(url).to_dict() == schema + assert Profile(url).to_dict() == schema @httpretty.activate def test_init_raises_if_url_isnt_a_json(self): @@ -58,7 +58,7 @@ def test_init_raises_if_url_isnt_a_json(self): httpretty.register_uri(httpretty.GET, url, body=body) with pytest.raises(datapackage.exceptions.SchemaError): - Schema(url).to_dict() + Profile(url).to_dict() @httpretty.activate def test_init_raises_if_url_doesnt_exist(self): @@ -66,32 +66,32 @@ def test_init_raises_if_url_doesnt_exist(self): httpretty.register_uri(httpretty.GET, url, status=404) with pytest.raises(datapackage.exceptions.SchemaError): - Schema(url).to_dict() + Profile(url).to_dict() def test_init_raises_if_schema_isnt_string_nor_dict(self): invalid_schema = [] with pytest.raises(datapackage.exceptions.SchemaError): - Schema(invalid_schema) + Profile(invalid_schema) def test_init_raises_if_schema_is_invalid(self): invalid_schema = { 'required': 51, } with pytest.raises(datapackage.exceptions.SchemaError): - Schema(invalid_schema) + Profile(invalid_schema) def test_to_dict_converts_schema_to_dict(self): original_schema_dict = { 'foo': 'bar', } - schema = Schema(original_schema_dict) + schema = Profile(original_schema_dict) assert schema.to_dict() == original_schema_dict def test_to_dict_modifying_the_dict_doesnt_modify_the_schema(self): original_schema_dict = { 'foo': 'bar', } - schema = Schema(original_schema_dict) + schema = Profile(original_schema_dict) schema_dict = schema.to_dict() schema_dict['bar'] = 'baz' assert 'bar' not in schema.to_dict() @@ -108,7 +108,7 @@ def test_validate(self): data = { 'name': 'Sample Package', } - schema = Schema(schema_dict) + schema = Profile(schema_dict) schema.validate(data) def test_validate_should_raise_when_invalid(self): @@ -121,7 +121,7 @@ def test_validate_should_raise_when_invalid(self): 'required': ['name'], } data = {} - schema = Schema(schema_dict) + schema = Profile(schema_dict) with pytest.raises(datapackage.exceptions.ValidationError): schema.validate(data) @@ -130,7 +130,7 @@ def test_it_creates_properties_for_every_toplevel_attribute(self): 'foo': 'bar', 'baz': [], } - schema = Schema(schema_dict) + schema = Profile(schema_dict) assert schema.foo == 'bar' assert schema.baz == [] @@ -138,19 +138,19 @@ def test_doesnt_allow_changing_schema_properties(self): schema_dict = { 'foo': 'bar', } - schema = Schema(schema_dict) + schema = Profile(schema_dict) with pytest.raises(AttributeError): schema.foo = 'baz' def test_allow_changing_properties_not_in_schema(self): schema_dict = {} - schema = Schema(schema_dict) + schema = Profile(schema_dict) schema.foo = 'bar' assert schema.foo == 'bar' def test_raises_if_trying_to_access_inexistent_attribute(self): schema_dict = {} - schema = Schema(schema_dict) + schema = Profile(schema_dict) with pytest.raises(AttributeError): schema.this_doesnt_exist @@ -160,7 +160,7 @@ def test_changing_properties_doesnt_change_the_originals(self): 'bar': [], } } - schema = Schema(schema_dict) + schema = Profile(schema_dict) schema.foo['bar'].append('baz') assert schema.foo == {'bar': []} @@ -168,7 +168,7 @@ def test_properties_are_visible_with_dir(self): schema_dict = { 'foo': {} } - schema = Schema(schema_dict) + schema = Profile(schema_dict) assert 'foo' in dir(schema) def test_schema_properties_doesnt_linger_in_class(self): @@ -178,8 +178,8 @@ def test_schema_properties_doesnt_linger_in_class(self): bar_schema_dict = { 'bar': {} } - foo_schema = Schema(foo_schema_dict) - bar_schema = Schema(bar_schema_dict) + foo_schema = Profile(foo_schema_dict) + bar_schema = Profile(bar_schema_dict) assert 'bar' not in dir(foo_schema) assert 'foo' not in dir(bar_schema) @@ -193,7 +193,7 @@ def test_iter_validation_returns_iter_with_each_validationerror(self): data_dict = [2, 3, 4] expected_errors_validators = ('maxItems', 'enum') - schema = Schema(schema_dict) + schema = Profile(schema_dict) errors = [error for error in schema.iter_errors(data_dict)] assert len(errors) == 2 @@ -205,7 +205,7 @@ def test_iter_validation_returns_no_errors_if_data_is_valid(self): schema_dict = {} data_dict = '' - schema = Schema(schema_dict) + schema = Profile(schema_dict) errors = [error for error in schema.iter_errors(data_dict)] assert len(errors) == 0