diff --git a/.editorconfig b/.editorconfig index b49ba59..aa2b421 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,15 +17,6 @@ insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 -# Python files -[*.py] -indent_size = 4 -# isort plugin configuration -known_first_party = datacite -known_standard_library = ssl -multi_line_output = 2 -default_section = THIRDPARTY - # RST files (used by sphinx) [*.rst] indent_size = 4 diff --git a/datacite/__init__.py b/datacite/__init__.py index c03199a..65f314b 100644 --- a/datacite/__init__.py +++ b/datacite/__init__.py @@ -16,4 +16,4 @@ __version__ = "1.1.4" -__all__ = ('DataCiteMDSClient', 'DataCiteRESTClient', '__version__') +__all__ = ("DataCiteMDSClient", "DataCiteRESTClient", "__version__") diff --git a/datacite/client.py b/datacite/client.py index 882c514..487104e 100644 --- a/datacite/client.py +++ b/datacite/client.py @@ -19,8 +19,8 @@ from .errors import DataCiteError from .request import DataCiteRequest -HTTP_OK = requests.codes['ok'] -HTTP_CREATED = requests.codes['created'] +HTTP_OK = requests.codes["ok"] +HTTP_CREATED = requests.codes["created"] class DataCiteMDSClient(object): @@ -30,8 +30,9 @@ class DataCiteMDSClient(object): developed. """ - def __init__(self, username, password, prefix, test_mode=False, url=None, - timeout=None): + def __init__( + self, username, password, prefix, test_mode=False, url=None, timeout=None + ): """Initialize the API client wrapper. :param username: DataCite username. @@ -51,14 +52,14 @@ def __init__(self, username, password, prefix, test_mode=False, url=None, else: self.api_url = url or "https://mds.datacite.org/" - if not self.api_url.endswith('/'): - self.api_url += '/' + if not self.api_url.endswith("/"): + self.api_url += "/" self.timeout = timeout def __repr__(self): """Create string representation of object.""" - return ''.format(self.username) + return "".format(self.username) def _create_request(self): """Create a new Request object.""" @@ -88,7 +89,7 @@ def doi_post(self, new_doi, location): :param location: URL where the resource is located. :return: "CREATED" or "HANDLE_ALREADY_EXISTS". """ - headers = {'Content-Type': 'text/plain;charset=UTF-8'} + headers = {"Content-Type": "text/plain;charset=UTF-8"} # Use \r\n for HTTP client data. body = "\r\n".join(["doi=%s" % new_doi, "url=%s" % location]) @@ -105,8 +106,7 @@ def metadata_get(self, doi): :param doi: DOI name of the resource. """ - headers = {'Accept': 'application/xml', - 'Accept-Encoding': 'UTF-8'} + headers = {"Accept": "application/xml", "Accept-Encoding": "UTF-8"} request = self._create_request() resp = request.get("metadata/" + doi, headers=headers) @@ -125,7 +125,9 @@ def metadata_post(self, metadata): :param metadata: XML format of the metadata. :return: "CREATED" or "HANDLE_ALREADY_EXISTS" """ - headers = {'Content-Type': 'application/xml;charset=UTF-8', } + headers = { + "Content-Type": "application/xml;charset=UTF-8", + } request = self._create_request() resp = request.post("metadata", body=metadata, headers=headers) @@ -174,7 +176,7 @@ def media_post(self, doi, media): :param media: Dictionary of (mime-type, URL) key/value pairs. :return: "OK" """ - headers = {'Content-Type': 'text/plain;charset=UTF-8'} + headers = {"Content-Type": "text/plain;charset=UTF-8"} # Use \r\n for HTTP client data. body = "\r\n".join(["%s=%s" % (k, v) for k, v in media.items()]) diff --git a/datacite/jsonutils.py b/datacite/jsonutils.py index fb7886e..f0ed7d6 100644 --- a/datacite/jsonutils.py +++ b/datacite/jsonutils.py @@ -11,19 +11,19 @@ """JSON utilities.""" import json + from jsonschema import RefResolver, validate from jsonschema.validators import validator_for def validator_factory(schema_filename): """Provide a JSON schema validator for a given schema file.""" - with open(schema_filename, 'r') as fp: + with open(schema_filename, "r") as fp: schema = json.load(fp) validator_cls = validator_for(schema) validator_cls.check_schema(schema) return validator_cls( - schema, - resolver=RefResolver('file:{}'.format(schema_filename), schema) + schema, resolver=RefResolver("file:{}".format(schema_filename), schema) ) diff --git a/datacite/request.py b/datacite/request.py index 72da5e9..60cec41 100644 --- a/datacite/request.py +++ b/datacite/request.py @@ -32,16 +32,22 @@ class DataCiteRequest(object): (connect, read) to specify each timeout individually. """ - def __init__(self, base_url=None, username=None, password=None, - default_params=None, timeout=None): + def __init__( + self, + base_url=None, + username=None, + password=None, + default_params=None, + timeout=None, + ): """Initialize request object.""" self.base_url = base_url self.username = username - self.password = password.encode('utf8') + self.password = password.encode("utf8") self.default_params = default_params or {} self.timeout = timeout - def request(self, url, method='GET', body=None, params=None, headers=None): + def request(self, url, method="GET", body=None, params=None, headers=None): """Make a request. If the request was successful (i.e no exceptions), you can find the @@ -66,7 +72,7 @@ def request(self, url, method='GET', body=None, params=None, headers=None): url = self.base_url + url if body and isinstance(body, str): - body = body.encode('utf-8') + body = body.encode("utf-8") request_func = getattr(requests, method.lower()) kwargs = dict( @@ -75,12 +81,12 @@ def request(self, url, method='GET', body=None, params=None, headers=None): headers=headers, ) - if method == 'POST': - kwargs['data'] = body - if method == 'PUT': - kwargs['data'] = body + if method == "POST": + kwargs["data"] = body + if method == "PUT": + kwargs["data"] = body if self.timeout is not None: - kwargs['timeout'] = self.timeout + kwargs["timeout"] = self.timeout try: return request_func(url, **kwargs) @@ -95,15 +101,16 @@ def get(self, url, params=None, headers=None): def post(self, url, body=None, params=None, headers=None): """Make a POST request.""" - return self.request(url, method="POST", body=body, params=params, - headers=headers) + return self.request( + url, method="POST", body=body, params=params, headers=headers + ) def put(self, url, body=None, params=None, headers=None): """Make a PUT request.""" - return self.request(url, method="PUT", body=body, params=params, - headers=headers) + return self.request( + url, method="PUT", body=body, params=params, headers=headers + ) def delete(self, url, params=None, headers=None): """Make a DELETE request.""" - return self.request(url, method="DELETE", params=params, - headers=headers) + return self.request(url, method="DELETE", params=params, headers=headers) diff --git a/datacite/rest_client.py b/datacite/rest_client.py index fb336a4..0858200 100644 --- a/datacite/rest_client.py +++ b/datacite/rest_client.py @@ -17,22 +17,24 @@ """ import json -import requests import warnings + +import requests from idutils import normalize_doi from .errors import DataCiteError from .request import DataCiteRequest -HTTP_OK = requests.codes['ok'] -HTTP_CREATED = requests.codes['created'] +HTTP_OK = requests.codes["ok"] +HTTP_CREATED = requests.codes["created"] class DataCiteRESTClient(object): """DataCite REST API client wrapper.""" - def __init__(self, username, password, prefix, test_mode=False, url=None, - timeout=None): + def __init__( + self, username, password, prefix, test_mode=False, url=None, timeout=None + ): """Initialize the REST client wrapper. :param username: DataCite username. @@ -52,14 +54,14 @@ def __init__(self, username, password, prefix, test_mode=False, url=None, else: self.api_url = url or "https://api.datacite.org/" - if not self.api_url.endswith('/'): - self.api_url += '/' + if not self.api_url.endswith("/"): + self.api_url += "/" self.timeout = timeout def __repr__(self): """Create string representation of object.""" - return ''.format(self.username) + return "".format(self.username) def _create_request(self): """Create a new Request object.""" @@ -87,7 +89,7 @@ def get_doi(self, doi): request = self._create_request() resp = request.get("dois/" + doi) if resp.status_code == HTTP_OK: - return resp.json()['data']['attributes']['url'] + return resp.json()["data"]["attributes"]["url"] else: raise DataCiteError.factory(resp.status_code, resp.text) @@ -98,38 +100,39 @@ def check_doi(self, doi): 12.12345/123 with the prefix defined """ # If prefix is in doi - if '/' in doi: - split = doi.split('/') + if "/" in doi: + split = doi.split("/") prefix = split[0] if prefix != self.prefix: # Provided a DOI with the wrong prefix - raise ValueError('Wrong DOI {0} prefix provided, it should be ' - '{1} as defined in the rest client' - .format(prefix, self.prefix)) + raise ValueError( + "Wrong DOI {0} prefix provided, it should be " + "{1} as defined in the rest client".format(prefix, self.prefix) + ) else: - doi = '{prefix}/{doi}'.format(prefix=self.prefix, doi=doi) + doi = "{prefix}/{doi}".format(prefix=self.prefix, doi=doi) return normalize_doi(doi) def post_doi(self, data): """Post a new JSON payload to DataCite.""" - headers = {'content-type': 'application/vnd.api+json'} + headers = {"content-type": "application/vnd.api+json"} body = {"data": data} request = self._create_request() resp = request.post("dois", body=json.dumps(body), headers=headers) if resp.status_code == HTTP_CREATED: - return resp.json()['data']['id'] + return resp.json()["data"]["id"] else: raise DataCiteError.factory(resp.status_code, resp.text) def put_doi(self, doi, data): """Put a JSON payload to DataCite for an existing DOI.""" - headers = {'content-type': 'application/vnd.api+json'} + headers = {"content-type": "application/vnd.api+json"} body = {"data": data} request = self._create_request() url = "dois/" + doi resp = request.put(url, body=json.dumps(body), headers=headers) if resp.status_code == HTTP_OK: - return resp.json()['data']['attributes'] + return resp.json()["data"]["attributes"] else: raise DataCiteError.factory(resp.status_code, resp.text) @@ -148,7 +151,7 @@ def draft_doi(self, metadata=None, doi=None): """ data = {"attributes": {}} if metadata: - data['attributes'] = metadata + data["attributes"] = metadata data["attributes"]["prefix"] = self.prefix if doi: doi = self.check_doi(doi) @@ -166,7 +169,7 @@ def update_url(self, doi, url): data = {"attributes": {"url": url}} result = self.put_doi(doi, data) - return result['url'] + return result["url"] def delete_doi(self, doi): """Delete a doi. @@ -220,7 +223,7 @@ def update_doi(self, doi, metadata=None, url=None): doi = self.check_doi(doi) data["attributes"]["doi"] = doi if metadata: - data['attributes'] = metadata + data["attributes"] = metadata if url: data["attributes"]["url"] = url @@ -300,12 +303,12 @@ def get_metadata(self, doi): :param doi: DOI name of the resource. """ """Put a JSON payload to DataCite for an existing DOI.""" - headers = {'content-type': 'application/vnd.api+json'} + headers = {"content-type": "application/vnd.api+json"} request = self._create_request() resp = request.get("dois/" + doi, headers=headers) if resp.status_code == HTTP_OK: - return resp.json()['data']['attributes'] + return resp.json()["data"]["attributes"] else: raise DataCiteError.factory(resp.status_code, resp.text) @@ -323,11 +326,11 @@ def get_media(self, doi): :param doi: DOI name of the resource. """ - headers = {'content-type': 'application/vnd.api+json'} + headers = {"content-type": "application/vnd.api+json"} request = self._create_request() resp = request.get("dois/" + doi, headers=headers) if resp.status_code == HTTP_OK: - return resp.json()['relationships']['media'] + return resp.json()["relationships"]["media"] else: raise DataCiteError.factory(resp.status_code, resp.text) diff --git a/datacite/schema31.py b/datacite/schema31.py index 31e6abe..cc5a054 100644 --- a/datacite/schema31.py +++ b/datacite/schema31.py @@ -15,27 +15,30 @@ from lxml.builder import E from .jsonutils import validator_factory -from .xmlutils import Rules, dump_etree_helper, etree_to_string, \ - set_elem_attr, set_non_empty_attr +from .xmlutils import ( + Rules, + dump_etree_helper, + etree_to_string, + set_elem_attr, + set_non_empty_attr, +) rules = Rules() ns = { - None: 'http://datacite.org/schema/kernel-3', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xml': 'xml', + None: "http://datacite.org/schema/kernel-3", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xml": "xml", } root_attribs = { - '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': - 'http://datacite.org/schema/kernel-3 ' - 'http://schema.datacite.org/meta/kernel-3/metadata.xsd', + "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://datacite.org/schema/kernel-3 " + "http://schema.datacite.org/meta/kernel-3/metadata.xsd", } -validator = validator_factory(pkg_resources.resource_filename( - 'datacite', - 'schemas/datacite-v3.1.json' -)) +validator = validator_factory( + pkg_resources.resource_filename("datacite", "schemas/datacite-v3.1.json") +) def dump_etree(data): @@ -53,33 +56,30 @@ def validate(data): return validator.is_valid(data) -@rules.rule('identifier') +@rules.rule("identifier") def identifier(path, value): """Transform identifier.""" - return E.identifier( - value['identifier'], - identifierType=value['identifierType'] - ) + return E.identifier(value["identifier"], identifierType=value["identifierType"]) def affiliation(root, value): """Extract affiliation.""" - val = value.get('affiliation') + val = value.get("affiliation") if val: root.append(E.affiliation(val)) def nameidentifier(root, value): """Extract nameidentifier.""" - val = value.get('nameIdentifier', {}) - if val.get('nameIdentifier'): - elem = E.nameIdentifier(value['nameIdentifier']['nameIdentifier']) - elem.set('nameIdentifierScheme', val['nameIdentifierScheme']) - set_elem_attr(elem, 'schemeURI', val) + val = value.get("nameIdentifier", {}) + if val.get("nameIdentifier"): + elem = E.nameIdentifier(value["nameIdentifier"]["nameIdentifier"]) + elem.set("nameIdentifierScheme", val["nameIdentifierScheme"]) + set_elem_attr(elem, "schemeURI", val) root.append(elem) -@rules.rule('creators') +@rules.rule("creators") def creators(path, values): """Transform creators.""" if not values: @@ -87,9 +87,7 @@ def creators(path, values): root = E.creators() for value in values: - creator = E.creator( - E.creatorName(value['creatorName']) - ) + creator = E.creator(E.creatorName(value["creatorName"])) nameidentifier(creator, value) affiliation(creator, value) @@ -98,7 +96,7 @@ def creators(path, values): return root -@rules.rule('titles') +@rules.rule("titles") def titles(path, values): """Transform titles.""" if not values: @@ -106,16 +104,16 @@ def titles(path, values): root = E.titles() for value in values: - elem = etree.Element('title', nsmap=ns) - elem.text = value['title'] - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_non_empty_attr(elem, 'titleType', value.get('titleType')) + elem = etree.Element("title", nsmap=ns) + elem.text = value["title"] + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_non_empty_attr(elem, "titleType", value.get("titleType")) root.append(elem) return root -@rules.rule('publisher') +@rules.rule("publisher") def publisher(path, value): """Transform publisher.""" if not value: @@ -123,7 +121,7 @@ def publisher(path, value): return E.publisher(value) -@rules.rule('publicationYear') +@rules.rule("publicationYear") def publication_year(path, value): """Transform publicationYear.""" if not value: @@ -131,7 +129,7 @@ def publication_year(path, value): return E.publicationYear(str(value)) -@rules.rule('subjects') +@rules.rule("subjects") def subjects(path, values): """Transform subjects.""" if not values: @@ -139,15 +137,15 @@ def subjects(path, values): root = E.subjects() for value in values: - elem = E.subject(value['subject']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'subjectScheme', value) + elem = E.subject(value["subject"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "subjectScheme", value) root.append(elem) return root -@rules.rule('contributors') +@rules.rule("contributors") def contributors(path, values): """Transform contributors.""" if not values: @@ -156,8 +154,8 @@ def contributors(path, values): root = E.contributors() for value in values: contributor = E.contributor( - E.contributorName(value['contributorName']), - contributorType=value['contributorType'] + E.contributorName(value["contributorName"]), + contributorType=value["contributorType"], ) nameidentifier(contributor, value) affiliation(contributor, value) @@ -166,7 +164,7 @@ def contributors(path, values): return root -@rules.rule('dates') +@rules.rule("dates") def dates(path, values): """Transform dates.""" if not values: @@ -174,12 +172,12 @@ def dates(path, values): root = E.dates() for value in values: - root.append(E.date(value['date'], dateType=value['dateType'])) + root.append(E.date(value["date"], dateType=value["dateType"])) return root -@rules.rule('language') +@rules.rule("language") def language(path, value): """Transform language.""" if not value: @@ -187,19 +185,19 @@ def language(path, value): return E.language(value) -@rules.rule('resourceType') +@rules.rule("resourceType") def resource_type(path, value): """Transform resourceType.""" if not value: return elem = E.resourceType() - elem.set('resourceTypeGeneral', value['resourceTypeGeneral']) - if value.get('resourceType'): - elem.text = value['resourceType'] + elem.set("resourceTypeGeneral", value["resourceTypeGeneral"]) + if value.get("resourceType"): + elem.text = value["resourceType"] return elem -@rules.rule('alternateIdentifiers') +@rules.rule("alternateIdentifiers") def alternate_identifiers(path, values): """Transform alternateIdenftifiers.""" if not values: @@ -207,14 +205,14 @@ def alternate_identifiers(path, values): root = E.alternateIdentifiers() for value in values: - elem = E.alternateIdentifier(value['alternateIdentifier']) - elem.set('alternateIdentifierType', value['alternateIdentifierType']) + elem = E.alternateIdentifier(value["alternateIdentifier"]) + elem.set("alternateIdentifierType", value["alternateIdentifierType"]) root.append(elem) return root -@rules.rule('relatedIdentifiers') +@rules.rule("relatedIdentifiers") def related_identifiers(path, values): """Transform relatedIdentifiers.""" if not values: @@ -223,12 +221,12 @@ def related_identifiers(path, values): root = E.relatedIdentifiers() for value in values: elem = E.relatedIdentifier() - elem.text = value['relatedIdentifier'] - elem.set('relatedIdentifierType', value['relatedIdentifierType']) - elem.set('relationType', value['relationType']) - set_elem_attr(elem, 'relatedMetadataScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'schemeType', value) + elem.text = value["relatedIdentifier"] + elem.set("relatedIdentifierType", value["relatedIdentifierType"]) + elem.set("relationType", value["relationType"]) + set_elem_attr(elem, "relatedMetadataScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "schemeType", value) root.append(elem) return root @@ -243,19 +241,19 @@ def free_text_list(plural, singular, values): return root -@rules.rule('sizes') +@rules.rule("sizes") def sizes(path, values): """Transform sizes.""" - return free_text_list('sizes', 'size', values) + return free_text_list("sizes", "size", values) -@rules.rule('formats') +@rules.rule("formats") def formats(path, values): """Transform sizes.""" - return free_text_list('formats', 'format', values) + return free_text_list("formats", "format", values) -@rules.rule('version') +@rules.rule("version") def version(path, value): """Transform version.""" if not value: @@ -263,7 +261,7 @@ def version(path, value): return E.version(value) -@rules.rule('rightsList') +@rules.rule("rightsList") def rights(path, values): """Transform rights.""" if not values: @@ -271,14 +269,14 @@ def rights(path, values): root = E.rightsList() for value in values: - elem = E.rights(value['rights']) - set_elem_attr(elem, 'rightsURI', value) + elem = E.rights(value["rights"]) + set_elem_attr(elem, "rightsURI", value) root.append(elem) return root -@rules.rule('descriptions') +@rules.rule("descriptions") def descriptions(path, values): """Transform descriptions.""" if not values: @@ -287,15 +285,15 @@ def descriptions(path, values): root = E.descriptions() for value in values: elem = E.description( - value['description'], descriptionType=value['descriptionType'] + value["description"], descriptionType=value["descriptionType"] ) - set_non_empty_attr(elem, '{xml}lang', value.get('language')) + set_non_empty_attr(elem, "{xml}lang", value.get("language")) root.append(elem) return root -@rules.rule('geoLocations') +@rules.rule("geoLocations") def geolocations(path, values): """Transform geolocations.""" if not values: @@ -305,15 +303,15 @@ def geolocations(path, values): for value in values: elem = E.geoLocation() - point = value.get('geoLocationPoint') + point = value.get("geoLocationPoint") if point: elem.append(E.geoLocationPoint(point)) - box = value.get('geoLocationBox') + box = value.get("geoLocationBox") if box: elem.append(E.geoLocationBox(box)) - place = value.get('geoLocationPlace') + place = value.get("geoLocationPlace") if place: elem.append(E.geoLocationPlace(place)) diff --git a/datacite/schema40.py b/datacite/schema40.py index 53a8676..e19927b 100644 --- a/datacite/schema40.py +++ b/datacite/schema40.py @@ -15,27 +15,30 @@ from lxml.builder import E from .jsonutils import validator_factory -from .xmlutils import Rules, dump_etree_helper, etree_to_string, \ - set_elem_attr, set_non_empty_attr +from .xmlutils import ( + Rules, + dump_etree_helper, + etree_to_string, + set_elem_attr, + set_non_empty_attr, +) rules = Rules() ns = { - None: 'http://datacite.org/schema/kernel-4', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xml': 'xml', + None: "http://datacite.org/schema/kernel-4", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xml": "xml", } root_attribs = { - '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': - 'http://datacite.org/schema/kernel-4 ' - 'http://schema.datacite.org/meta/kernel-4/metadata.xsd', + "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://datacite.org/schema/kernel-4 " + "http://schema.datacite.org/meta/kernel-4/metadata.xsd", } -validator = validator_factory(pkg_resources.resource_filename( - 'datacite', - 'schemas/datacite-v4.0.json' -)) +validator = validator_factory( + pkg_resources.resource_filename("datacite", "schemas/datacite-v4.0.json") +) def dump_etree(data): @@ -53,48 +56,45 @@ def validate(data): return validator.is_valid(data) -@rules.rule('identifier') +@rules.rule("identifier") def identifier(path, value): """Transform identifier.""" - return E.identifier( - value['identifier'], - identifierType=value['identifierType'] - ) + return E.identifier(value["identifier"], identifierType=value["identifierType"]) def affiliations(root, values): """Extract affiliation.""" - vals = values.get('affiliations', []) + vals = values.get("affiliations", []) for val in vals: root.append(E.affiliation(val)) def familyname(root, value): """Extract family name.""" - val = value.get('familyName') + val = value.get("familyName") if val: root.append(E.familyName(val)) def givenname(root, value): """Extract family name.""" - val = value.get('givenName') + val = value.get("givenName") if val: root.append(E.givenName(val)) def nameidentifiers(root, values): """Extract nameidentifier.""" - vals = values.get('nameIdentifiers', []) + vals = values.get("nameIdentifiers", []) for val in vals: - if val.get('nameIdentifier'): - elem = E.nameIdentifier(val['nameIdentifier']) - elem.set('nameIdentifierScheme', val['nameIdentifierScheme']) - set_elem_attr(elem, 'schemeURI', val) + if val.get("nameIdentifier"): + elem = E.nameIdentifier(val["nameIdentifier"]) + elem.set("nameIdentifierScheme", val["nameIdentifierScheme"]) + set_elem_attr(elem, "schemeURI", val) root.append(elem) -@rules.rule('creators') +@rules.rule("creators") def creators(path, values): """Transform creators.""" if not values: @@ -102,9 +102,7 @@ def creators(path, values): root = E.creators() for value in values: - creator = E.creator( - E.creatorName(value['creatorName']) - ) + creator = E.creator(E.creatorName(value["creatorName"])) givenname(creator, value) familyname(creator, value) @@ -115,7 +113,7 @@ def creators(path, values): return root -@rules.rule('titles') +@rules.rule("titles") def titles(path, values): """Transform titles.""" if not values: @@ -123,20 +121,20 @@ def titles(path, values): root = E.titles() for value in values: - elem = etree.Element('title', nsmap=ns) - elem.text = value['title'] - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + elem = etree.Element("title", nsmap=ns) + elem.text = value["title"] + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) # FIXME: 'type' was in initial 4.0 version, which is now supported # for backwards compatibility until version 5 is released. - set_non_empty_attr(elem, 'titleType', value.get('type')) + set_non_empty_attr(elem, "titleType", value.get("type")) # 'titleType' will supersede 'type' if available - set_non_empty_attr(elem, 'titleType', value.get('titleType')) + set_non_empty_attr(elem, "titleType", value.get("titleType")) root.append(elem) return root -@rules.rule('publisher') +@rules.rule("publisher") def publisher(path, value): """Transform publisher.""" if not value: @@ -144,7 +142,7 @@ def publisher(path, value): return E.publisher(value) -@rules.rule('publicationYear') +@rules.rule("publicationYear") def publication_year(path, value): """Transform publicationYear.""" if not value: @@ -152,7 +150,7 @@ def publication_year(path, value): return E.publicationYear(str(value)) -@rules.rule('subjects') +@rules.rule("subjects") def subjects(path, values): """Transform subjects.""" if not values: @@ -160,16 +158,16 @@ def subjects(path, values): root = E.subjects() for value in values: - elem = E.subject(value['subject']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_elem_attr(elem, 'subjectScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'valueURI', value) + elem = E.subject(value["subject"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_elem_attr(elem, "subjectScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "valueURI", value) root.append(elem) return root -@rules.rule('contributors') +@rules.rule("contributors") def contributors(path, values): """Transform contributors.""" if not values: @@ -178,8 +176,8 @@ def contributors(path, values): root = E.contributors() for value in values: contributor = E.contributor( - E.contributorName(value['contributorName']), - contributorType=value['contributorType'] + E.contributorName(value["contributorName"]), + contributorType=value["contributorType"], ) givenname(contributor, value) familyname(contributor, value) @@ -190,7 +188,7 @@ def contributors(path, values): return root -@rules.rule('dates') +@rules.rule("dates") def dates(path, values): """Transform dates.""" if not values: @@ -198,12 +196,12 @@ def dates(path, values): root = E.dates() for value in values: - root.append(E.date(value['date'], dateType=value['dateType'])) + root.append(E.date(value["date"], dateType=value["dateType"])) return root -@rules.rule('language') +@rules.rule("language") def language(path, value): """Transform language.""" if not value: @@ -211,17 +209,17 @@ def language(path, value): return E.language(value) -@rules.rule('resourceType') +@rules.rule("resourceType") def resource_type(path, value): """Transform resourceType.""" elem = E.resourceType() - elem.set('resourceTypeGeneral', value['resourceTypeGeneral']) - if value.get('resourceType'): - elem.text = value['resourceType'] + elem.set("resourceTypeGeneral", value["resourceTypeGeneral"]) + if value.get("resourceType"): + elem.text = value["resourceType"] return elem -@rules.rule('alternateIdentifiers') +@rules.rule("alternateIdentifiers") def alternate_identifiers(path, values): """Transform alternateIdenftifiers.""" if not values: @@ -229,14 +227,14 @@ def alternate_identifiers(path, values): root = E.alternateIdentifiers() for value in values: - elem = E.alternateIdentifier(value['alternateIdentifier']) - elem.set('alternateIdentifierType', value['alternateIdentifierType']) + elem = E.alternateIdentifier(value["alternateIdentifier"]) + elem.set("alternateIdentifierType", value["alternateIdentifierType"]) root.append(elem) return root -@rules.rule('relatedIdentifiers') +@rules.rule("relatedIdentifiers") def related_identifiers(path, values): """Transform relatedIdentifiers.""" if not values: @@ -245,12 +243,12 @@ def related_identifiers(path, values): root = E.relatedIdentifiers() for value in values: elem = E.relatedIdentifier() - elem.text = value['relatedIdentifier'] - elem.set('relatedIdentifierType', value['relatedIdentifierType']) - elem.set('relationType', value['relationType']) - set_elem_attr(elem, 'relatedMetadataScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'schemeType', value) + elem.text = value["relatedIdentifier"] + elem.set("relatedIdentifierType", value["relatedIdentifierType"]) + elem.set("relationType", value["relationType"]) + set_elem_attr(elem, "relatedMetadataScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "schemeType", value) root.append(elem) return root @@ -265,19 +263,19 @@ def free_text_list(plural, singular, values): return root -@rules.rule('sizes') +@rules.rule("sizes") def sizes(path, values): """Transform sizes.""" - return free_text_list('sizes', 'size', values) + return free_text_list("sizes", "size", values) -@rules.rule('formats') +@rules.rule("formats") def formats(path, values): """Transform sizes.""" - return free_text_list('formats', 'format', values) + return free_text_list("formats", "format", values) -@rules.rule('version') +@rules.rule("version") def version(path, value): """Transform version.""" if not value: @@ -285,7 +283,7 @@ def version(path, value): return E.version(value) -@rules.rule('rightsList') +@rules.rule("rightsList") def rights(path, values): """Transform rights.""" if not values: @@ -293,14 +291,14 @@ def rights(path, values): root = E.rightsList() for value in values: - elem = E.rights(value['rights']) - set_elem_attr(elem, 'rightsURI', value) + elem = E.rights(value["rights"]) + set_elem_attr(elem, "rightsURI", value) root.append(elem) return root -@rules.rule('descriptions') +@rules.rule("descriptions") def descriptions(path, values): """Transform descriptions.""" if not values: @@ -309,15 +307,15 @@ def descriptions(path, values): root = E.descriptions() for value in values: elem = E.description( - value['description'], descriptionType=value['descriptionType'] + value["description"], descriptionType=value["descriptionType"] ) - set_non_empty_attr(elem, '{xml}lang', value.get('language')) + set_non_empty_attr(elem, "{xml}lang", value.get("language")) root.append(elem) return root -@rules.rule('fundingReferences') +@rules.rule("fundingReferences") def fundingreferences(path, values): """Transform funding references.""" if not values: @@ -327,22 +325,21 @@ def fundingreferences(path, values): for value in values: element = E.fundingReference() - element.append(E.funderName(value.get('funderName'))) + element.append(E.funderName(value.get("funderName"))) - identifier = value.get('funderIdentifier') + identifier = value.get("funderIdentifier") if identifier: - elem = E.funderIdentifier(identifier['funderIdentifier']) - elem.set('funderIdentifierType', - identifier['funderIdentifierType']) + elem = E.funderIdentifier(identifier["funderIdentifier"]) + elem.set("funderIdentifierType", identifier["funderIdentifierType"]) element.append(elem) - number = value.get('awardNumber') + number = value.get("awardNumber") if number: - elem = E.awardNumber(number['awardNumber']) - set_elem_attr(elem, 'awardURI', number) + elem = E.awardNumber(number["awardNumber"]) + set_elem_attr(elem, "awardURI", number) element.append(elem) - title = value.get('awardTitle') + title = value.get("awardTitle") if title: element.append(E.awardTitle(title)) if len(element): @@ -352,11 +349,11 @@ def fundingreferences(path, values): def geopoint(root, value): """Extract a point (either geoLocationPoint or polygonPoint).""" - root.append(E.pointLongitude(str(value['pointLongitude']))) - root.append(E.pointLatitude(str(value['pointLatitude']))) + root.append(E.pointLongitude(str(value["pointLongitude"]))) + root.append(E.pointLatitude(str(value["pointLatitude"]))) -@rules.rule('geoLocations') +@rules.rule("geoLocations") def geolocations(path, values): """Transform geolocations.""" if not values: @@ -366,26 +363,26 @@ def geolocations(path, values): for value in values: element = E.geoLocation() - place = value.get('geoLocationPlace') + place = value.get("geoLocationPlace") if place: element.append(E.geoLocationPlace(place)) - point = value.get('geoLocationPoint') + point = value.get("geoLocationPoint") if point: elem = E.geoLocationPoint() geopoint(elem, point) element.append(elem) - box = value.get('geoLocationBox') + box = value.get("geoLocationBox") if box: elem = E.geoLocationBox() - elem.append(E.westBoundLongitude(str(box['westBoundLongitude']))) - elem.append(E.eastBoundLongitude(str(box['eastBoundLongitude']))) - elem.append(E.southBoundLatitude(str(box['southBoundLatitude']))) - elem.append(E.northBoundLatitude(str(box['northBoundLatitude']))) + elem.append(E.westBoundLongitude(str(box["westBoundLongitude"]))) + elem.append(E.eastBoundLongitude(str(box["eastBoundLongitude"]))) + elem.append(E.southBoundLatitude(str(box["southBoundLatitude"]))) + elem.append(E.northBoundLatitude(str(box["northBoundLatitude"]))) element.append(elem) - polygon = value.get('geoLocationPolygon') + polygon = value.get("geoLocationPolygon") if polygon: elem = E.geoLocationPolygon() points = polygon["polygonPoints"] diff --git a/datacite/schema41.py b/datacite/schema41.py index a5437f9..66fd165 100644 --- a/datacite/schema41.py +++ b/datacite/schema41.py @@ -15,27 +15,30 @@ from lxml.builder import E from .jsonutils import validator_factory -from .xmlutils import Rules, dump_etree_helper, etree_to_string, \ - set_elem_attr, set_non_empty_attr +from .xmlutils import ( + Rules, + dump_etree_helper, + etree_to_string, + set_elem_attr, + set_non_empty_attr, +) rules = Rules() ns = { - None: 'http://datacite.org/schema/kernel-4', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xml': 'xml', + None: "http://datacite.org/schema/kernel-4", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xml": "xml", } root_attribs = { - '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': - 'http://datacite.org/schema/kernel-4 ' - 'http://schema.datacite.org/meta/kernel-4.1/metadata.xsd', + "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://datacite.org/schema/kernel-4 " + "http://schema.datacite.org/meta/kernel-4.1/metadata.xsd", } -validator = validator_factory(pkg_resources.resource_filename( - 'datacite', - 'schemas/datacite-v4.1.json' -)) +validator = validator_factory( + pkg_resources.resource_filename("datacite", "schemas/datacite-v4.1.json") +) def dump_etree(data): @@ -53,32 +56,29 @@ def validate(data): return validator.is_valid(data) -@rules.rule('identifier') +@rules.rule("identifier") def identifier(path, value): """Transform identifier.""" - return E.identifier( - value['identifier'], - identifierType=value['identifierType'] - ) + return E.identifier(value["identifier"], identifierType=value["identifierType"]) def affiliations(root, values): """Extract affiliation.""" - vals = values.get('affiliations', []) + vals = values.get("affiliations", []) for val in vals: root.append(E.affiliation(val)) def familyname(root, value): """Extract family name.""" - val = value.get('familyName') + val = value.get("familyName") if val: root.append(E.familyName(val)) def givenname(root, value): """Extract family name.""" - val = value.get('givenName') + val = value.get("givenName") if val: root.append(E.givenName(val)) @@ -86,22 +86,22 @@ def givenname(root, value): def person_or_org_name(root, value, tagname): """Extract creator/contributor name and it's 'nameType' attribute.""" elem = E(tagname, value[tagname]) - set_elem_attr(elem, 'nameType', value) + set_elem_attr(elem, "nameType", value) root.append(elem) def nameidentifiers(root, values): """Extract nameidentifier.""" - vals = values.get('nameIdentifiers', []) + vals = values.get("nameIdentifiers", []) for val in vals: - if val.get('nameIdentifier'): - elem = E.nameIdentifier(val['nameIdentifier']) - elem.set('nameIdentifierScheme', val['nameIdentifierScheme']) - set_elem_attr(elem, 'schemeURI', val) + if val.get("nameIdentifier"): + elem = E.nameIdentifier(val["nameIdentifier"]) + elem.set("nameIdentifierScheme", val["nameIdentifierScheme"]) + set_elem_attr(elem, "schemeURI", val) root.append(elem) -@rules.rule('creators') +@rules.rule("creators") def creators(path, values): """Transform creators.""" if not values: @@ -110,7 +110,7 @@ def creators(path, values): root = E.creators() for value in values: creator = E.creator() - person_or_org_name(creator, value, 'creatorName') + person_or_org_name(creator, value, "creatorName") givenname(creator, value) familyname(creator, value) nameidentifiers(creator, value) @@ -120,7 +120,7 @@ def creators(path, values): return root -@rules.rule('titles') +@rules.rule("titles") def titles(path, values): """Transform titles.""" if not values: @@ -128,20 +128,20 @@ def titles(path, values): root = E.titles() for value in values: - elem = etree.Element('title', nsmap=ns) - elem.text = value['title'] - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + elem = etree.Element("title", nsmap=ns) + elem.text = value["title"] + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) # 'type' was a mistake in 4.0 serializer, which is supported # for backwards compatibility until kernel 5 is released. - set_non_empty_attr(elem, 'titleType', value.get('type')) + set_non_empty_attr(elem, "titleType", value.get("type")) # 'titleType' will supersede 'type' if available - set_non_empty_attr(elem, 'titleType', value.get('titleType')) + set_non_empty_attr(elem, "titleType", value.get("titleType")) root.append(elem) return root -@rules.rule('publisher') +@rules.rule("publisher") def publisher(path, value): """Transform publisher.""" if not value: @@ -149,7 +149,7 @@ def publisher(path, value): return E.publisher(value) -@rules.rule('publicationYear') +@rules.rule("publicationYear") def publication_year(path, value): """Transform publicationYear.""" if not value: @@ -157,7 +157,7 @@ def publication_year(path, value): return E.publicationYear(str(value)) -@rules.rule('subjects') +@rules.rule("subjects") def subjects(path, values): """Transform subjects.""" if not values: @@ -165,16 +165,16 @@ def subjects(path, values): root = E.subjects() for value in values: - elem = E.subject(value['subject']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_elem_attr(elem, 'subjectScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'valueURI', value) + elem = E.subject(value["subject"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_elem_attr(elem, "subjectScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "valueURI", value) root.append(elem) return root -@rules.rule('contributors') +@rules.rule("contributors") def contributors(path, values): """Transform contributors.""" if not values: @@ -183,8 +183,8 @@ def contributors(path, values): root = E.contributors() for value in values: contributor = E.contributor() - person_or_org_name(contributor, value, 'contributorName') - set_elem_attr(contributor, 'contributorType', value) + person_or_org_name(contributor, value, "contributorName") + set_elem_attr(contributor, "contributorType", value) givenname(contributor, value) familyname(contributor, value) nameidentifiers(contributor, value) @@ -194,7 +194,7 @@ def contributors(path, values): return root -@rules.rule('dates') +@rules.rule("dates") def dates(path, values): """Transform dates.""" if not values: @@ -202,14 +202,14 @@ def dates(path, values): root = E.dates() for value in values: - elem = E.date(value['date'], dateType=value['dateType']) - set_elem_attr(elem, 'dateInformation', value) + elem = E.date(value["date"], dateType=value["dateType"]) + set_elem_attr(elem, "dateInformation", value) root.append(elem) return root -@rules.rule('language') +@rules.rule("language") def language(path, value): """Transform language.""" if not value: @@ -217,17 +217,17 @@ def language(path, value): return E.language(value) -@rules.rule('resourceType') +@rules.rule("resourceType") def resource_type(path, value): """Transform resourceType.""" elem = E.resourceType() - elem.set('resourceTypeGeneral', value['resourceTypeGeneral']) - if value.get('resourceType'): - elem.text = value['resourceType'] + elem.set("resourceTypeGeneral", value["resourceTypeGeneral"]) + if value.get("resourceType"): + elem.text = value["resourceType"] return elem -@rules.rule('alternateIdentifiers') +@rules.rule("alternateIdentifiers") def alternate_identifiers(path, values): """Transform alternateIdenftifiers.""" if not values: @@ -235,14 +235,14 @@ def alternate_identifiers(path, values): root = E.alternateIdentifiers() for value in values: - elem = E.alternateIdentifier(value['alternateIdentifier']) - elem.set('alternateIdentifierType', value['alternateIdentifierType']) + elem = E.alternateIdentifier(value["alternateIdentifier"]) + elem.set("alternateIdentifierType", value["alternateIdentifierType"]) root.append(elem) return root -@rules.rule('relatedIdentifiers') +@rules.rule("relatedIdentifiers") def related_identifiers(path, values): """Transform relatedIdentifiers.""" if not values: @@ -251,13 +251,13 @@ def related_identifiers(path, values): root = E.relatedIdentifiers() for value in values: elem = E.relatedIdentifier() - elem.text = value['relatedIdentifier'] - elem.set('relatedIdentifierType', value['relatedIdentifierType']) - elem.set('relationType', value['relationType']) - set_elem_attr(elem, 'relatedMetadataScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'schemeType', value) - set_elem_attr(elem, 'resourceTypeGeneral', value) + elem.text = value["relatedIdentifier"] + elem.set("relatedIdentifierType", value["relatedIdentifierType"]) + elem.set("relationType", value["relationType"]) + set_elem_attr(elem, "relatedMetadataScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "schemeType", value) + set_elem_attr(elem, "resourceTypeGeneral", value) root.append(elem) return root @@ -272,19 +272,19 @@ def free_text_list(plural, singular, values): return root -@rules.rule('sizes') +@rules.rule("sizes") def sizes(path, values): """Transform sizes.""" - return free_text_list('sizes', 'size', values) + return free_text_list("sizes", "size", values) -@rules.rule('formats') +@rules.rule("formats") def formats(path, values): """Transform sizes.""" - return free_text_list('formats', 'format', values) + return free_text_list("formats", "format", values) -@rules.rule('version') +@rules.rule("version") def version(path, value): """Transform version.""" if not value: @@ -292,7 +292,7 @@ def version(path, value): return E.version(value) -@rules.rule('rightsList') +@rules.rule("rightsList") def rights(path, values): """Transform rights.""" if not values: @@ -300,15 +300,15 @@ def rights(path, values): root = E.rightsList() for value in values: - elem = E.rights(value['rights']) - set_elem_attr(elem, 'rightsURI', value) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + elem = E.rights(value["rights"]) + set_elem_attr(elem, "rightsURI", value) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) return root -@rules.rule('descriptions') +@rules.rule("descriptions") def descriptions(path, values): """Transform descriptions.""" if not values: @@ -317,15 +317,15 @@ def descriptions(path, values): root = E.descriptions() for value in values: elem = E.description( - value['description'], descriptionType=value['descriptionType'] + value["description"], descriptionType=value["descriptionType"] ) - set_non_empty_attr(elem, '{xml}lang', value.get('language')) + set_non_empty_attr(elem, "{xml}lang", value.get("language")) root.append(elem) return root -@rules.rule('fundingReferences') +@rules.rule("fundingReferences") def fundingreferences(path, values): """Transform funding references.""" if not values: @@ -335,22 +335,21 @@ def fundingreferences(path, values): for value in values: element = E.fundingReference() - element.append(E.funderName(value.get('funderName'))) + element.append(E.funderName(value.get("funderName"))) - identifier = value.get('funderIdentifier') + identifier = value.get("funderIdentifier") if identifier: - elem = E.funderIdentifier(identifier['funderIdentifier']) - elem.set('funderIdentifierType', - identifier['funderIdentifierType']) + elem = E.funderIdentifier(identifier["funderIdentifier"]) + elem.set("funderIdentifierType", identifier["funderIdentifierType"]) element.append(elem) - number = value.get('awardNumber') + number = value.get("awardNumber") if number: - elem = E.awardNumber(number['awardNumber']) - set_elem_attr(elem, 'awardURI', number) + elem = E.awardNumber(number["awardNumber"]) + set_elem_attr(elem, "awardURI", number) element.append(elem) - title = value.get('awardTitle') + title = value.get("awardTitle") if title: element.append(E.awardTitle(title)) if len(element): @@ -360,11 +359,11 @@ def fundingreferences(path, values): def geopoint(root, value): """Extract a point (either geoLocationPoint or polygonPoint).""" - root.append(E.pointLongitude(str(value['pointLongitude']))) - root.append(E.pointLatitude(str(value['pointLatitude']))) + root.append(E.pointLongitude(str(value["pointLongitude"]))) + root.append(E.pointLatitude(str(value["pointLatitude"]))) -@rules.rule('geoLocations') +@rules.rule("geoLocations") def geolocations(path, values): """Transform geolocations.""" if not values: @@ -374,28 +373,28 @@ def geolocations(path, values): for value in values: element = E.geoLocation() - place = value.get('geoLocationPlace') + place = value.get("geoLocationPlace") if place: element.append(E.geoLocationPlace(place)) - point = value.get('geoLocationPoint') + point = value.get("geoLocationPoint") if point: elem = E.geoLocationPoint() geopoint(elem, point) element.append(elem) - box = value.get('geoLocationBox') + box = value.get("geoLocationBox") if box: elem = E.geoLocationBox() - elem.append(E.westBoundLongitude(str(box['westBoundLongitude']))) - elem.append(E.eastBoundLongitude(str(box['eastBoundLongitude']))) - elem.append(E.southBoundLatitude(str(box['southBoundLatitude']))) - elem.append(E.northBoundLatitude(str(box['northBoundLatitude']))) + elem.append(E.westBoundLongitude(str(box["westBoundLongitude"]))) + elem.append(E.eastBoundLongitude(str(box["eastBoundLongitude"]))) + elem.append(E.southBoundLatitude(str(box["southBoundLatitude"]))) + elem.append(E.northBoundLatitude(str(box["northBoundLatitude"]))) element.append(elem) # Single-polygon supported for backwards compatibility. # FIXME: should no longer be accepted in kernel 5. - polygon = value.get('geoLocationPolygon') + polygon = value.get("geoLocationPolygon") if polygon: elem = E.geoLocationPolygon() points = polygon["polygonPoints"] @@ -405,7 +404,7 @@ def geolocations(path, values): elem.append(e) element.append(elem) - polygons = value.get('geoLocationPolygons', []) + polygons = value.get("geoLocationPolygons", []) for polygon in polygons: elem = E.geoLocationPolygon() points = polygon["polygonPoints"] diff --git a/datacite/schema42.py b/datacite/schema42.py index f48a2d6..bb57476 100644 --- a/datacite/schema42.py +++ b/datacite/schema42.py @@ -16,27 +16,30 @@ from lxml.builder import E from .jsonutils import validator_factory -from .xmlutils import Rules, dump_etree_helper, etree_to_string, \ - set_elem_attr, set_non_empty_attr +from .xmlutils import ( + Rules, + dump_etree_helper, + etree_to_string, + set_elem_attr, + set_non_empty_attr, +) rules = Rules() ns = { - None: 'http://datacite.org/schema/kernel-4', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xml': 'xml', + None: "http://datacite.org/schema/kernel-4", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xml": "xml", } root_attribs = { - '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': - 'http://datacite.org/schema/kernel-4 ' - 'http://schema.datacite.org/meta/kernel-4.2/metadata.xsd', + "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://datacite.org/schema/kernel-4 " + "http://schema.datacite.org/meta/kernel-4.2/metadata.xsd", } -validator = validator_factory(pkg_resources.resource_filename( - 'datacite', - 'schemas/datacite-v4.2.json' -)) +validator = validator_factory( + pkg_resources.resource_filename("datacite", "schemas/datacite-v4.2.json") +) def dump_etree(data): @@ -54,35 +57,32 @@ def validate(data): return validator.is_valid(data) -@rules.rule('identifiers') +@rules.rule("identifiers") def identifiers(path, values): """Transform identifiers to alternateIdentifiers and identifier.""" """ We assume there will only be 1 DOI identifier for the record. Any other identifiers are alternative identifiers. """ - alt = '' - doi = '' + alt = "" + doi = "" for value in values: - if value['identifierType'] == 'DOI': - if doi != '': + if value["identifierType"] == "DOI": + if doi != "": # Don't know what to do with two DOIs # Which is the actual identifier? raise TypeError - doi = E.identifier( - value['identifier'], - identifierType='DOI' - ) + doi = E.identifier(value["identifier"], identifierType="DOI") else: - if alt == '': + if alt == "": alt = E.alternateIdentifiers() - elem = E.alternateIdentifier(value['identifier']) - elem.set('alternateIdentifierType', value['identifierType']) + elem = E.alternateIdentifier(value["identifier"]) + elem.set("alternateIdentifierType", value["identifierType"]) alt.append(elem) - if alt == '': + if alt == "": # If we only have the DOI return doi - elif doi == '': + elif doi == "": # If we only have alt IDs return alt else: @@ -91,22 +91,22 @@ def identifiers(path, values): def affiliations(root, values): """Extract affiliation.""" - vals = values.get('affiliations', []) + vals = values.get("affiliations", []) for val in vals: - elem = E.affiliation(val['affiliation']) + elem = E.affiliation(val["affiliation"]) root.append(elem) def familyname(root, value): """Extract family name.""" - val = value.get('familyName') + val = value.get("familyName") if val: root.append(E.familyName(val)) def givenname(root, value): """Extract family name.""" - val = value.get('givenName') + val = value.get("givenName") if val: root.append(E.givenName(val)) @@ -114,23 +114,23 @@ def givenname(root, value): def person_or_org_name(root, value, xml_tagname, json_tagname): """Extract creator/contributor name and it's 'nameType' attribute.""" elem = E(xml_tagname, value[json_tagname]) - set_elem_attr(elem, 'nameType', value) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + set_elem_attr(elem, "nameType", value) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) def nameidentifiers(root, values): """Extract nameidentifier.""" - vals = values.get('nameIdentifiers', []) + vals = values.get("nameIdentifiers", []) for val in vals: - if val.get('nameIdentifier'): - elem = E.nameIdentifier(val['nameIdentifier']) - elem.set('nameIdentifierScheme', val['nameIdentifierScheme']) - set_elem_attr(elem, 'schemeURI', val) + if val.get("nameIdentifier"): + elem = E.nameIdentifier(val["nameIdentifier"]) + elem.set("nameIdentifierScheme", val["nameIdentifierScheme"]) + set_elem_attr(elem, "schemeURI", val) root.append(elem) -@rules.rule('creators') +@rules.rule("creators") def creators(path, values): """Transform creators.""" if not values: @@ -139,7 +139,7 @@ def creators(path, values): root = E.creators() for value in values: creator = E.creator() - person_or_org_name(creator, value, 'creatorName', 'name') + person_or_org_name(creator, value, "creatorName", "name") givenname(creator, value) familyname(creator, value) nameidentifiers(creator, value) @@ -149,7 +149,7 @@ def creators(path, values): return root -@rules.rule('titles') +@rules.rule("titles") def titles(path, values): """Transform titles.""" if not values: @@ -157,20 +157,20 @@ def titles(path, values): root = E.titles() for value in values: - elem = etree.Element('title', nsmap=ns) - elem.text = value['title'] - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + elem = etree.Element("title", nsmap=ns) + elem.text = value["title"] + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) # 'type' was a mistake in 4.0 serializer, which is supported # for backwards compatibility until kernel 5 is released. - set_non_empty_attr(elem, 'titleType', value.get('type')) + set_non_empty_attr(elem, "titleType", value.get("type")) # 'titleType' will supersede 'type' if available - set_non_empty_attr(elem, 'titleType', value.get('titleType')) + set_non_empty_attr(elem, "titleType", value.get("titleType")) root.append(elem) return root -@rules.rule('publisher') +@rules.rule("publisher") def publisher(path, value): """Transform publisher.""" if not value: @@ -178,7 +178,7 @@ def publisher(path, value): return E.publisher(value) -@rules.rule('publicationYear') +@rules.rule("publicationYear") def publication_year(path, value): """Transform publicationYear.""" if not value: @@ -186,7 +186,7 @@ def publication_year(path, value): return E.publicationYear(str(value)) -@rules.rule('subjects') +@rules.rule("subjects") def subjects(path, values): """Transform subjects.""" if not values: @@ -194,16 +194,16 @@ def subjects(path, values): root = E.subjects() for value in values: - elem = E.subject(value['subject']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_elem_attr(elem, 'subjectScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'valueURI', value) + elem = E.subject(value["subject"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_elem_attr(elem, "subjectScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "valueURI", value) root.append(elem) return root -@rules.rule('contributors') +@rules.rule("contributors") def contributors(path, values): """Transform contributors.""" if not values: @@ -212,8 +212,8 @@ def contributors(path, values): root = E.contributors() for value in values: contributor = E.contributor() - person_or_org_name(contributor, value, 'contributorName', 'name') - set_elem_attr(contributor, 'contributorType', value) + person_or_org_name(contributor, value, "contributorName", "name") + set_elem_attr(contributor, "contributorType", value) givenname(contributor, value) familyname(contributor, value) nameidentifiers(contributor, value) @@ -223,7 +223,7 @@ def contributors(path, values): return root -@rules.rule('dates') +@rules.rule("dates") def dates(path, values): """Transform dates.""" if not values: @@ -231,14 +231,14 @@ def dates(path, values): root = E.dates() for value in values: - elem = E.date(value['date'], dateType=value['dateType']) - set_elem_attr(elem, 'dateInformation', value) + elem = E.date(value["date"], dateType=value["dateType"]) + set_elem_attr(elem, "dateInformation", value) root.append(elem) return root -@rules.rule('language') +@rules.rule("language") def language(path, value): """Transform language.""" if not value: @@ -246,16 +246,16 @@ def language(path, value): return E.language(value) -@rules.rule('types') +@rules.rule("types") def resource_type(path, value): """Transform resourceType.""" elem = E.resourceType() - elem.set('resourceTypeGeneral', value['resourceTypeGeneral']) - elem.text = value['resourceType'] + elem.set("resourceTypeGeneral", value["resourceTypeGeneral"]) + elem.text = value["resourceType"] return elem -@rules.rule('relatedIdentifiers') +@rules.rule("relatedIdentifiers") def related_identifiers(path, values): """Transform relatedIdentifiers.""" if not values: @@ -264,13 +264,13 @@ def related_identifiers(path, values): root = E.relatedIdentifiers() for value in values: elem = E.relatedIdentifier() - elem.text = value['relatedIdentifier'] - elem.set('relatedIdentifierType', value['relatedIdentifierType']) - elem.set('relationType', value['relationType']) - set_elem_attr(elem, 'relatedMetadataScheme', value) - set_elem_attr(elem, 'schemeURI', value) - set_elem_attr(elem, 'schemeType', value) - set_elem_attr(elem, 'resourceTypeGeneral', value) + elem.text = value["relatedIdentifier"] + elem.set("relatedIdentifierType", value["relatedIdentifierType"]) + elem.set("relationType", value["relationType"]) + set_elem_attr(elem, "relatedMetadataScheme", value) + set_elem_attr(elem, "schemeURI", value) + set_elem_attr(elem, "schemeType", value) + set_elem_attr(elem, "resourceTypeGeneral", value) root.append(elem) return root @@ -285,19 +285,19 @@ def free_text_list(plural, singular, values): return root -@rules.rule('sizes') +@rules.rule("sizes") def sizes(path, values): """Transform sizes.""" - return free_text_list('sizes', 'size', values) + return free_text_list("sizes", "size", values) -@rules.rule('formats') +@rules.rule("formats") def formats(path, values): """Transform sizes.""" - return free_text_list('formats', 'format', values) + return free_text_list("formats", "format", values) -@rules.rule('version') +@rules.rule("version") def version(path, value): """Transform version.""" if not value: @@ -305,7 +305,7 @@ def version(path, value): return E.version(value) -@rules.rule('rightsList') +@rules.rule("rightsList") def rights(path, values): """Transform rights.""" if not values: @@ -313,21 +313,21 @@ def rights(path, values): root = E.rightsList() for value in values: - if 'rights' in value: - elem = E.rights(value['rights']) + if "rights" in value: + elem = E.rights(value["rights"]) # Handle the odd case where no rights text present else: elem = E.rights() - set_elem_attr(elem, 'rightsURI', value) - set_elem_attr(elem, 'rightsIdentifierScheme', value) - set_elem_attr(elem, 'rightsIdentifier', value) - set_elem_attr(elem, 'schemeURI', value) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + set_elem_attr(elem, "rightsURI", value) + set_elem_attr(elem, "rightsIdentifierScheme", value) + set_elem_attr(elem, "rightsIdentifier", value) + set_elem_attr(elem, "schemeURI", value) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) return root -@rules.rule('descriptions') +@rules.rule("descriptions") def descriptions(path, values): """Transform descriptions.""" if not values: @@ -336,9 +336,9 @@ def descriptions(path, values): root = E.descriptions() for value in values: elem = E.description( - value['description'], descriptionType=value['descriptionType'] + value["description"], descriptionType=value["descriptionType"] ) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) return root @@ -346,11 +346,11 @@ def descriptions(path, values): def geopoint(root, value): """Extract a point (either geoLocationPoint or polygonPoint).""" - root.append(E.pointLongitude(str(value['pointLongitude']))) - root.append(E.pointLatitude(str(value['pointLatitude']))) + root.append(E.pointLongitude(str(value["pointLongitude"]))) + root.append(E.pointLatitude(str(value["pointLatitude"]))) -@rules.rule('geoLocations') +@rules.rule("geoLocations") def geolocations(path, values): """Transform geolocations.""" if not values: @@ -360,26 +360,26 @@ def geolocations(path, values): for value in values: element = E.geoLocation() - place = value.get('geoLocationPlace') + place = value.get("geoLocationPlace") if place: element.append(E.geoLocationPlace(place)) - point = value.get('geoLocationPoint') + point = value.get("geoLocationPoint") if point: elem = E.geoLocationPoint() geopoint(elem, point) element.append(elem) - box = value.get('geoLocationBox') + box = value.get("geoLocationBox") if box: elem = E.geoLocationBox() - elem.append(E.westBoundLongitude(str(box['westBoundLongitude']))) - elem.append(E.eastBoundLongitude(str(box['eastBoundLongitude']))) - elem.append(E.southBoundLatitude(str(box['southBoundLatitude']))) - elem.append(E.northBoundLatitude(str(box['northBoundLatitude']))) + elem.append(E.westBoundLongitude(str(box["westBoundLongitude"]))) + elem.append(E.eastBoundLongitude(str(box["eastBoundLongitude"]))) + elem.append(E.southBoundLatitude(str(box["southBoundLatitude"]))) + elem.append(E.northBoundLatitude(str(box["northBoundLatitude"]))) element.append(elem) - polygons = value.get('geoLocationPolygons', []) + polygons = value.get("geoLocationPolygons", []) for polygon in polygons: elem = E.geoLocationPolygon() points = polygon["polygonPoints"] @@ -398,7 +398,7 @@ def geolocations(path, values): return root -@rules.rule('fundingReferences') +@rules.rule("fundingReferences") def fundingreferences(path, values): """Transform funding references.""" if not values: @@ -408,25 +408,25 @@ def fundingreferences(path, values): for value in values: element = E.fundingReference() - element.append(E.funderName(value.get('funderName'))) + element.append(E.funderName(value.get("funderName"))) - identifier = value.get('funderIdentifier') + identifier = value.get("funderIdentifier") if identifier: elem = E.funderIdentifier(identifier) - typev = value.get('funderIdentifierType') + typev = value.get("funderIdentifierType") if typev: - elem.set('funderIdentifierType', typev) + elem.set("funderIdentifierType", typev) element.append(elem) - number = value.get('awardNumber') + number = value.get("awardNumber") if number: elem = E.awardNumber(number) - uri = value.get('awardURI') + uri = value.get("awardURI") if uri: - elem.set('awardURI', uri) + elem.set("awardURI", uri) element.append(elem) - title = value.get('awardTitle') + title = value.get("awardTitle") if title: element.append(E.awardTitle(title)) if len(element): diff --git a/datacite/schema43.py b/datacite/schema43.py index ff5a466..5bef9f0 100644 --- a/datacite/schema43.py +++ b/datacite/schema43.py @@ -16,27 +16,30 @@ from lxml.builder import E from .jsonutils import validator_factory -from .xmlutils import Rules, dump_etree_helper, etree_to_string, \ - set_elem_attr, set_non_empty_attr +from .xmlutils import ( + Rules, + dump_etree_helper, + etree_to_string, + set_elem_attr, + set_non_empty_attr, +) rules = Rules() ns = { - None: 'http://datacite.org/schema/kernel-4', - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xml': 'xml', + None: "http://datacite.org/schema/kernel-4", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "xml": "xml", } root_attribs = { - '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': - 'http://datacite.org/schema/kernel-4 ' - 'http://schema.datacite.org/meta/kernel-4.3/metadata.xsd', + "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": "http://datacite.org/schema/kernel-4 " + "http://schema.datacite.org/meta/kernel-4.3/metadata.xsd", } -validator = validator_factory(pkg_resources.resource_filename( - 'datacite', - 'schemas/datacite-v4.3.json' -)) +validator = validator_factory( + pkg_resources.resource_filename("datacite", "schemas/datacite-v4.3.json") +) def dump_etree(data): @@ -54,35 +57,32 @@ def validate(data): return validator.is_valid(data) -@rules.rule('identifiers') +@rules.rule("identifiers") def identifiers(path, values): """Transform identifiers to alternateIdentifiers and identifier. We assume there will only be 1 DOI identifier for the record. Any other identifiers are alternative identifiers. """ - alt = '' - doi = '' + alt = "" + doi = "" for value in values: - if value['identifierType'] == 'DOI': - if doi != '': + if value["identifierType"] == "DOI": + if doi != "": # Don't know what to do with two DOIs # Which is the actual identifier? raise TypeError - doi = E.identifier( - value['identifier'], - identifierType='DOI' - ) + doi = E.identifier(value["identifier"], identifierType="DOI") else: - if alt == '': + if alt == "": alt = E.alternateIdentifiers() - elem = E.alternateIdentifier(value['identifier']) - elem.set('alternateIdentifierType', value['identifierType']) + elem = E.alternateIdentifier(value["identifier"]) + elem.set("alternateIdentifierType", value["identifierType"]) alt.append(elem) - if alt == '': + if alt == "": # If we only have the DOI return doi - elif doi == '': + elif doi == "": # If we only have alt IDs return alt else: @@ -91,29 +91,29 @@ def identifiers(path, values): def affiliation(root, values): """Extract affiliation.""" - vals = values.get('affiliation', []) + vals = values.get("affiliation", []) for val in vals: - if val.get('name'): - elem = E.affiliation(val['name']) + if val.get("name"): + elem = E.affiliation(val["name"]) # affiliationIdentifier metadata as Attributes # (0-1 cardinality, instead of 0-n as list of objects) - set_elem_attr(elem, 'affiliationIdentifier', val) - set_elem_attr(elem, 'affiliationIdentifierScheme', val) - if val.get('schemeUri'): - elem.set('schemeURI', val['schemeUri']) + set_elem_attr(elem, "affiliationIdentifier", val) + set_elem_attr(elem, "affiliationIdentifierScheme", val) + if val.get("schemeUri"): + elem.set("schemeURI", val["schemeUri"]) root.append(elem) def familyname(root, value): """Extract family name.""" - val = value.get('familyName') + val = value.get("familyName") if val: root.append(E.familyName(val)) def givenname(root, value): """Extract family name.""" - val = value.get('givenName') + val = value.get("givenName") if val: root.append(E.givenName(val)) @@ -121,24 +121,24 @@ def givenname(root, value): def person_or_org_name(root, value, xml_tagname, json_tagname): """Extract creator/contributor name and it's 'nameType' attribute.""" elem = E(xml_tagname, value[json_tagname]) - set_elem_attr(elem, 'nameType', value) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + set_elem_attr(elem, "nameType", value) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) def nameidentifiers(root, values): """Extract nameidentifier.""" - vals = values.get('nameIdentifiers', []) + vals = values.get("nameIdentifiers", []) for val in vals: - if val.get('nameIdentifier'): - elem = E.nameIdentifier(val['nameIdentifier']) - elem.set('nameIdentifierScheme', val['nameIdentifierScheme']) - if val.get('schemeUri'): - elem.set('schemeURI', val['schemeUri']) + if val.get("nameIdentifier"): + elem = E.nameIdentifier(val["nameIdentifier"]) + elem.set("nameIdentifierScheme", val["nameIdentifierScheme"]) + if val.get("schemeUri"): + elem.set("schemeURI", val["schemeUri"]) root.append(elem) -@rules.rule('creators') +@rules.rule("creators") def creators(path, values): """Transform creators.""" if not values: @@ -147,7 +147,7 @@ def creators(path, values): root = E.creators() for value in values: creator = E.creator() - person_or_org_name(creator, value, 'creatorName', 'name') + person_or_org_name(creator, value, "creatorName", "name") givenname(creator, value) familyname(creator, value) nameidentifiers(creator, value) @@ -157,7 +157,7 @@ def creators(path, values): return root -@rules.rule('titles') +@rules.rule("titles") def titles(path, values): """Transform titles.""" if not values: @@ -165,20 +165,20 @@ def titles(path, values): root = E.titles() for value in values: - elem = etree.Element('title', nsmap=ns) - elem.text = value['title'] - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + elem = etree.Element("title", nsmap=ns) + elem.text = value["title"] + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) # 'type' was a mistake in 4.0 serializer, which is supported # for backwards compatibility until kernel 5 is released. - set_non_empty_attr(elem, 'titleType', value.get('type')) + set_non_empty_attr(elem, "titleType", value.get("type")) # 'titleType' will supersede 'type' if available - set_non_empty_attr(elem, 'titleType', value.get('titleType')) + set_non_empty_attr(elem, "titleType", value.get("titleType")) root.append(elem) return root -@rules.rule('publisher') +@rules.rule("publisher") def publisher(path, value): """Transform publisher.""" if not value: @@ -186,7 +186,7 @@ def publisher(path, value): return E.publisher(value) -@rules.rule('publicationYear') +@rules.rule("publicationYear") def publication_year(path, value): """Transform publicationYear.""" if not value: @@ -194,7 +194,7 @@ def publication_year(path, value): return E.publicationYear(str(value)) -@rules.rule('subjects') +@rules.rule("subjects") def subjects(path, values): """Transform subjects.""" if not values: @@ -202,18 +202,18 @@ def subjects(path, values): root = E.subjects() for value in values: - elem = E.subject(value['subject']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) - set_elem_attr(elem, 'subjectScheme', value) - if value.get('schemeUri'): - elem.set('schemeURI', value['schemeUri']) - if value.get('valueUri'): - elem.set('valueURI', value['valueUri']) + elem = E.subject(value["subject"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) + set_elem_attr(elem, "subjectScheme", value) + if value.get("schemeUri"): + elem.set("schemeURI", value["schemeUri"]) + if value.get("valueUri"): + elem.set("valueURI", value["valueUri"]) root.append(elem) return root -@rules.rule('contributors') +@rules.rule("contributors") def contributors(path, values): """Transform contributors.""" if not values: @@ -222,8 +222,8 @@ def contributors(path, values): root = E.contributors() for value in values: contributor = E.contributor() - person_or_org_name(contributor, value, 'contributorName', 'name') - set_elem_attr(contributor, 'contributorType', value) + person_or_org_name(contributor, value, "contributorName", "name") + set_elem_attr(contributor, "contributorType", value) givenname(contributor, value) familyname(contributor, value) nameidentifiers(contributor, value) @@ -233,7 +233,7 @@ def contributors(path, values): return root -@rules.rule('dates') +@rules.rule("dates") def dates(path, values): """Transform dates.""" if not values: @@ -241,14 +241,14 @@ def dates(path, values): root = E.dates() for value in values: - elem = E.date(value['date'], dateType=value['dateType']) - set_elem_attr(elem, 'dateInformation', value) + elem = E.date(value["date"], dateType=value["dateType"]) + set_elem_attr(elem, "dateInformation", value) root.append(elem) return root -@rules.rule('language') +@rules.rule("language") def language(path, value): """Transform language.""" if not value: @@ -256,16 +256,16 @@ def language(path, value): return E.language(value) -@rules.rule('types') +@rules.rule("types") def resource_type(path, value): """Transform resourceType.""" elem = E.resourceType() - elem.set('resourceTypeGeneral', value['resourceTypeGeneral']) - elem.text = value['resourceType'] + elem.set("resourceTypeGeneral", value["resourceTypeGeneral"]) + elem.text = value["resourceType"] return elem -@rules.rule('relatedIdentifiers') +@rules.rule("relatedIdentifiers") def related_identifiers(path, values): """Transform relatedIdentifiers.""" if not values: @@ -274,14 +274,14 @@ def related_identifiers(path, values): root = E.relatedIdentifiers() for value in values: elem = E.relatedIdentifier() - elem.text = value['relatedIdentifier'] - elem.set('relatedIdentifierType', value['relatedIdentifierType']) - elem.set('relationType', value['relationType']) - set_elem_attr(elem, 'relatedMetadataScheme', value) - if value.get('schemeUri'): - elem.set('schemeURI', value['schemeUri']) - set_elem_attr(elem, 'schemeType', value) - set_elem_attr(elem, 'resourceTypeGeneral', value) + elem.text = value["relatedIdentifier"] + elem.set("relatedIdentifierType", value["relatedIdentifierType"]) + elem.set("relationType", value["relationType"]) + set_elem_attr(elem, "relatedMetadataScheme", value) + if value.get("schemeUri"): + elem.set("schemeURI", value["schemeUri"]) + set_elem_attr(elem, "schemeType", value) + set_elem_attr(elem, "resourceTypeGeneral", value) root.append(elem) return root @@ -296,19 +296,19 @@ def free_text_list(plural, singular, values): return root -@rules.rule('sizes') +@rules.rule("sizes") def sizes(path, values): """Transform sizes.""" - return free_text_list('sizes', 'size', values) + return free_text_list("sizes", "size", values) -@rules.rule('formats') +@rules.rule("formats") def formats(path, values): """Transform sizes.""" - return free_text_list('formats', 'format', values) + return free_text_list("formats", "format", values) -@rules.rule('version') +@rules.rule("version") def version(path, value): """Transform version.""" if not value: @@ -316,7 +316,7 @@ def version(path, value): return E.version(value) -@rules.rule('rightsList') +@rules.rule("rightsList") def rights(path, values): """Transform rights.""" if not values: @@ -324,23 +324,23 @@ def rights(path, values): root = E.rightsList() for value in values: - if 'rights' in value: - elem = E.rights(value['rights']) + if "rights" in value: + elem = E.rights(value["rights"]) # Handle the odd case where no rights text present else: elem = E.rights() - if value.get('rightsUri'): - elem.set('rightsURI', value['rightsUri']) - set_elem_attr(elem, 'rightsIdentifierScheme', value) - set_elem_attr(elem, 'rightsIdentifier', value) - if value.get('schemeUri'): - elem.set('schemeURI', value['schemeUri']) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + if value.get("rightsUri"): + elem.set("rightsURI", value["rightsUri"]) + set_elem_attr(elem, "rightsIdentifierScheme", value) + set_elem_attr(elem, "rightsIdentifier", value) + if value.get("schemeUri"): + elem.set("schemeURI", value["schemeUri"]) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) return root -@rules.rule('descriptions') +@rules.rule("descriptions") def descriptions(path, values): """Transform descriptions.""" if not values: @@ -349,9 +349,9 @@ def descriptions(path, values): root = E.descriptions() for value in values: elem = E.description( - value['description'], descriptionType=value['descriptionType'] + value["description"], descriptionType=value["descriptionType"] ) - set_non_empty_attr(elem, '{xml}lang', value.get('lang')) + set_non_empty_attr(elem, "{xml}lang", value.get("lang")) root.append(elem) return root @@ -359,11 +359,11 @@ def descriptions(path, values): def geopoint(root, value): """Extract a point (either geoLocationPoint or polygonPoint).""" - root.append(E.pointLongitude(str(value['pointLongitude']))) - root.append(E.pointLatitude(str(value['pointLatitude']))) + root.append(E.pointLongitude(str(value["pointLongitude"]))) + root.append(E.pointLatitude(str(value["pointLatitude"]))) -@rules.rule('geoLocations') +@rules.rule("geoLocations") def geolocations(path, values): """Transform geolocations.""" if not values: @@ -373,26 +373,26 @@ def geolocations(path, values): for value in values: element = E.geoLocation() - place = value.get('geoLocationPlace') + place = value.get("geoLocationPlace") if place: element.append(E.geoLocationPlace(place)) - point = value.get('geoLocationPoint') + point = value.get("geoLocationPoint") if point: elem = E.geoLocationPoint() geopoint(elem, point) element.append(elem) - box = value.get('geoLocationBox') + box = value.get("geoLocationBox") if box: elem = E.geoLocationBox() - elem.append(E.westBoundLongitude(str(box['westBoundLongitude']))) - elem.append(E.eastBoundLongitude(str(box['eastBoundLongitude']))) - elem.append(E.southBoundLatitude(str(box['southBoundLatitude']))) - elem.append(E.northBoundLatitude(str(box['northBoundLatitude']))) + elem.append(E.westBoundLongitude(str(box["westBoundLongitude"]))) + elem.append(E.eastBoundLongitude(str(box["eastBoundLongitude"]))) + elem.append(E.southBoundLatitude(str(box["southBoundLatitude"]))) + elem.append(E.northBoundLatitude(str(box["northBoundLatitude"]))) element.append(elem) - polygons = value.get('geoLocationPolygons', []) + polygons = value.get("geoLocationPolygons", []) for polygon in polygons: elem = E.geoLocationPolygon() points = polygon["polygonPoints"] @@ -411,7 +411,7 @@ def geolocations(path, values): return root -@rules.rule('fundingReferences') +@rules.rule("fundingReferences") def fundingreferences(path, values): """Transform funding references.""" if not values: @@ -421,25 +421,25 @@ def fundingreferences(path, values): for value in values: element = E.fundingReference() - element.append(E.funderName(value.get('funderName'))) + element.append(E.funderName(value.get("funderName"))) - identifier = value.get('funderIdentifier') + identifier = value.get("funderIdentifier") if identifier: elem = E.funderIdentifier(identifier) - typev = value.get('funderIdentifierType') + typev = value.get("funderIdentifierType") if typev: - elem.set('funderIdentifierType', typev) + elem.set("funderIdentifierType", typev) element.append(elem) - number = value.get('awardNumber') + number = value.get("awardNumber") if number: elem = E.awardNumber(number) - uri = value.get('awardUri') + uri = value.get("awardUri") if uri: - elem.set('awardURI', uri) + elem.set("awardURI", uri) element.append(elem) - title = value.get('awardTitle') + title = value.get("awardTitle") if title: element.append(E.awardTitle(title)) if len(element): diff --git a/datacite/xmlutils.py b/datacite/xmlutils.py index f793a86..b2eaad8 100644 --- a/datacite/xmlutils.py +++ b/datacite/xmlutils.py @@ -12,6 +12,7 @@ from collections import OrderedDict + from lxml import etree @@ -20,7 +21,7 @@ def dump_etree_helper(data, rules, nsmap, attrib): JSON should be validated before it is given to to_xml. """ - output = etree.Element('resource', nsmap=nsmap, attrib=attrib) + output = etree.Element("resource", nsmap=nsmap, attrib=attrib) for rule in rules: if rule not in data: @@ -38,15 +39,14 @@ def dump_etree_helper(data, rules, nsmap, attrib): return output -def etree_to_string(root, pretty_print=True, xml_declaration=True, - encoding='utf-8'): +def etree_to_string(root, pretty_print=True, xml_declaration=True, encoding="utf-8"): """Dump XML etree as a string.""" return etree.tostring( root, pretty_print=pretty_print, xml_declaration=xml_declaration, encoding=encoding, - ).decode('utf-8') + ).decode("utf-8") def set_elem_attr(element, attrib, data): @@ -78,10 +78,11 @@ def __iter__(self): def rule(self, key): """Decorate as a rule for a key in top level JSON.""" + def register(f): if key in self.rules: - raise ValueError( - 'Rule for "{0}" already registered'.format(key)) + raise ValueError('Rule for "{0}" already registered'.format(key)) self.rules[key] = f return f + return register diff --git a/docs/conf.py b/docs/conf.py index 46a5c93..890d421 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,251 +12,246 @@ import sphinx.environment from docutils.utils import get_source_line + from datacite import __version__ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Do not warn on external images. -suppress_warnings = ['image.nonlocal_uri'] +suppress_warnings = ["image.nonlocal_uri"] # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'datacite' -copyright = u'2015-2016, CERN' +project = "datacite" +copyright = "2015-2016, CERN" # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -#html_theme = 'default' -html_theme = 'alabaster' +# html_theme = 'default' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - 'description': 'Python API wrapper for the DataCite Metadata Store API and' - ' DataCite XML generation.', - 'github_user': 'inveniosoftware', - 'github_repo': 'datacite', - 'github_button': False, - 'github_banner': True, - 'show_powered_by': False, - 'extra_nav_links': { - 'datacite@GitHub': 'http://github.com/inveniosoftware/datacite', - 'datacite@PyPI': 'http://pypi.python.org/pypi/datacite/', - } + "description": "Python API wrapper for the DataCite Metadata Store API and" + " DataCite XML generation.", + "github_user": "inveniosoftware", + "github_repo": "datacite", + "github_button": False, + "github_banner": True, + "show_powered_by": False, + "extra_nav_links": { + "datacite@GitHub": "http://github.com/inveniosoftware/datacite", + "datacite@PyPI": "http://pypi.python.org/pypi/datacite/", + }, } html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - 'donate.html', + "**": [ + "about.html", + "navigation.html", + "relations.html", + "searchbox.html", + "donate.html", ] } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'DataCitedoc' +htmlhelp_basename = "DataCitedoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'DataCite.tex', u'DataCite Documentation', - u'Invenio Software', 'manual'), + ("index", "DataCite.tex", "DataCite Documentation", "Invenio Software", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'datacite', u'DataCite Documentation', - [u'Invenio Software'], 1) -] +man_pages = [("index", "datacite", "DataCite Documentation", ["Invenio Software"], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -265,19 +260,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'DataCite', u'DataCite Documentation', - u'Invenio Software', 'DataCite', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "DataCite", + "DataCite Documentation", + "Invenio Software", + "DataCite", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/tests/conftest.py b/tests/conftest.py index 3b8b9f2..2a064e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,17 +11,20 @@ """Pytest configuration.""" import json +from os.path import dirname, join + import pytest import responses from lxml import etree -from os.path import dirname, join def pytest_addoption(parser): """Add option to run tests that require password.""" parser.addoption( - "--runpw", action="store_true", default=False, - help="run tests that require password" + "--runpw", + action="store_true", + default=False, + help="run tests that require password", ) @@ -40,10 +43,7 @@ def pytest_collection_modifyitems(config, items): def example_json_file(): """Load DataCite v3.1 full example JSON.""" path = dirname(__file__) - with open(join( - path, - 'data', - 'datacite-v3.1-full-example.json')) as file: + with open(join(path, "data", "datacite-v3.1-full-example.json")) as file: return file.read() @@ -51,10 +51,7 @@ def example_json_file(): def example_json_file40(): """Load DataCite v4.0 full example JSON.""" path = dirname(__file__) - with open(join( - path, - 'data', - 'datacite-v4.0-full-example.json')) as file: + with open(join(path, "data", "datacite-v4.0-full-example.json")) as file: return file.read() @@ -62,10 +59,7 @@ def example_json_file40(): def example_json_file41(): """Load DataCite v4.1 full example JSON.""" path = dirname(__file__) - with open(join( - path, - 'data', - 'datacite-v4.1-full-example.json')) as file: + with open(join(path, "data", "datacite-v4.1-full-example.json")) as file: return file.read() @@ -73,10 +67,7 @@ def example_json_file41(): def example_json_file42(): """Load DataCite v4.2 full example JSON.""" path = dirname(__file__) - with open(join( - path, - 'data', - 'datacite-v4.2-full-example.json')) as file: + with open(join(path, "data", "datacite-v4.2-full-example.json")) as file: return file.read() @@ -84,10 +75,7 @@ def example_json_file42(): def example_json_file43(): """Load DataCite v4.3 full example JSON.""" path = dirname(__file__) - with open(join( - path, - 'data', - 'datacite-v4.3-full-example.json')) as file: + with open(join(path, "data", "datacite-v4.3-full-example.json")) as file: return file.read() @@ -124,7 +112,7 @@ def example_json43(example_json_file43): def load_xml(filename): """Helper method for loading the XML example file.""" path = dirname(__file__) - with open(join(path, 'data', filename)) as file: + with open(join(path, "data", filename)) as file: content = file.read() return content @@ -132,152 +120,143 @@ def load_xml(filename): @pytest.fixture def example_xml_file(): """Load DataCite v3.1 full example XML.""" - return load_xml('datacite-v3.1-full-example.xml') + return load_xml("datacite-v3.1-full-example.xml") @pytest.fixture def example_xml_file40(): """Load DataCite v4.0 full example XML.""" - return load_xml('datacite-v4.0-full-example.xml') + return load_xml("datacite-v4.0-full-example.xml") @pytest.fixture def example_xml_file41(): """Load DataCite v4.1 full example XML.""" - return load_xml('datacite-v4.1-full-example.xml') + return load_xml("datacite-v4.1-full-example.xml") @pytest.fixture def example_xml_file42(): """Load DataCite v4.2 full example XML.""" - return load_xml('datacite-v4.2-full-example.xml') + return load_xml("datacite-v4.2-full-example.xml") @pytest.fixture def example_xml_file43(): """Load DataCite v4.3 full example XML.""" - return load_xml('datacite-v4.3-full-example.xml') + return load_xml("datacite-v4.3-full-example.xml") @pytest.fixture def example_xml(example_xml_file): """Load DataCite v3.1 full example as an etree.""" - return etree.fromstring(example_xml_file.encode('utf-8')) + return etree.fromstring(example_xml_file.encode("utf-8")) @pytest.fixture def example_xml40(example_xml_file40): """Load DataCite v4.0 full example as an etree.""" - return etree.fromstring(example_xml_file40.encode('utf-8')) + return etree.fromstring(example_xml_file40.encode("utf-8")) @pytest.fixture def example_xml41(example_xml_file41): """Load DataCite v4.1 full example as an etree.""" - return etree.fromstring(example_xml_file41.encode('utf-8')) + return etree.fromstring(example_xml_file41.encode("utf-8")) @pytest.fixture def example_xml42(example_xml_file41): """Load DataCite v4.2 full example as an etree.""" - return etree.fromstring(example_xml_file42.encode('utf-8')) + return etree.fromstring(example_xml_file42.encode("utf-8")) @pytest.fixture def example_xml43(example_xml_file41): """Load DataCite v4.3 full example as an etree.""" - return etree.fromstring(example_xml_file43.encode('utf-8')) + return etree.fromstring(example_xml_file43.encode("utf-8")) def _load_xsd(xsd_filename): """Load one of the XSD schemas.""" - with open(join(dirname(__file__), 'data', 'xml.xsd')) as fp: + with open(join(dirname(__file__), "data", "xml.xsd")) as fp: xmlxsd = fp.read() # Ensure the schema validator doesn't make any http requests. - responses.add( - responses.GET, - 'https://www.w3.org/2009/01/xml.xsd', - body=xmlxsd) + responses.add(responses.GET, "https://www.w3.org/2009/01/xml.xsd", body=xmlxsd) return etree.XMLSchema( - file='file://' + join(dirname(__file__), 'data', xsd_filename) + file="file://" + join(dirname(__file__), "data", xsd_filename) ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def xsd31(): """Load DataCite v3.1 full example as an etree.""" - return _load_xsd('metadata31.xsd') + return _load_xsd("metadata31.xsd") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def xsd40(): """Load DataCite v4.0 full example as an etree.""" - return _load_xsd('metadata40.xsd') + return _load_xsd("metadata40.xsd") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def xsd41(): """Load DataCite v4.1 full example as an etree.""" - return _load_xsd('metadata41.xsd') + return _load_xsd("metadata41.xsd") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def xsd42(): """Load DataCite v4.2 full example as an etree.""" - return _load_xsd('4.2/metadata.xsd') + return _load_xsd("4.2/metadata.xsd") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def xsd43(): """Load DataCite v4.3 full example as an etree.""" - return _load_xsd('4.3/metadata.xsd') + return _load_xsd("4.3/metadata.xsd") -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def minimal_json42(): """Minimal valid JSON for DataCite 4.2.""" return { - 'identifiers': [{ - 'identifierType': 'DOI', - 'identifier': '10.1234/foo.bar', - }], - 'creators': [ - {'name': 'Nielsen, Lars Holm'}, + "identifiers": [ + { + "identifierType": "DOI", + "identifier": "10.1234/foo.bar", + } ], - 'titles': [ - {'title': 'Minimal Test Case'} + "creators": [ + {"name": "Nielsen, Lars Holm"}, ], - 'publisher': 'Invenio Software', - 'publicationYear': '2016', - 'types': { - 'resourceType': '', - 'resourceTypeGeneral': 'Software' - }, - 'schemaVersion': 'http://datacite.org/schema/kernel-4' + "titles": [{"title": "Minimal Test Case"}], + "publisher": "Invenio Software", + "publicationYear": "2016", + "types": {"resourceType": "", "resourceTypeGeneral": "Software"}, + "schemaVersion": "http://datacite.org/schema/kernel-4", } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def minimal_json43(): """Minimal valid JSON for DataCite 4.3.""" return { - 'identifiers': [{ - 'identifierType': 'DOI', - 'identifier': '10.1234/foo.bar', - }], - 'creators': [ - {'name': 'Nielsen, Lars Holm'}, + "identifiers": [ + { + "identifierType": "DOI", + "identifier": "10.1234/foo.bar", + } ], - 'titles': [ - {'title': 'Minimal Test Case'} + "creators": [ + {"name": "Nielsen, Lars Holm"}, ], - 'publisher': 'Invenio Software', - 'publicationYear': '2016', - 'types': { - 'resourceType': '', - 'resourceTypeGeneral': 'Software' - }, - 'schemaVersion': 'http://datacite.org/schema/kernel-4' + "titles": [{"title": "Minimal Test Case"}], + "publisher": "Invenio Software", + "publicationYear": "2016", + "types": {"resourceType": "", "resourceTypeGeneral": "Software"}, + "schemaVersion": "http://datacite.org/schema/kernel-4", } diff --git a/tests/helpers.py b/tests/helpers.py index 7c91050..b3b1615 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -21,8 +21,7 @@ RESTURL = "https://doi.example.org/" -def get_client(username="DC", password="pw", prefix='10.1234', - with_fake_url=True): +def get_client(username="DC", password="pw", prefix="10.1234", with_fake_url=True): """Create a API client.""" client = DataCiteMDSClient( username=username, @@ -36,8 +35,7 @@ def get_client(username="DC", password="pw", prefix='10.1234', return client -def get_rest(username="DC", password="pw", prefix='10.1234', - with_fake_url=True): +def get_rest(username="DC", password="pw", prefix="10.1234", with_fake_url=True): """Create a REST API client.""" client = DataCiteRESTClient( username=username, @@ -62,7 +60,7 @@ def get_credentials(): def load_xml_path(path): """Helper method for loading an XML example file from a path.""" path_base = dirname(__file__) - with io.open(join(path_base, path), encoding='utf-8') as file: + with io.open(join(path_base, path), encoding="utf-8") as file: content = file.read() return content @@ -70,7 +68,7 @@ def load_xml_path(path): def load_json_path(path): """Helper method for loading a JSON example file from a path.""" path_base = dirname(__file__) - with io.open(join(path_base, path), encoding='utf-8') as file: + with io.open(join(path_base, path), encoding="utf-8") as file: content = file.read() return json.loads(content) @@ -79,21 +77,21 @@ def load_json_path(path): # Tests on example files # TEST_43_JSON_FILES = [ - 'data/4.3/datacite-example-polygon-v4.json', - 'data/4.3/datacite-example-fundingReference-v4.json', - 'data/4.3/datacite-example-ResearchGroup_Methods-v4.json', - 'data/4.3/datacite-example-complicated-v4.json', - 'data/4.3/datacite-example-datapaper-v4.json', - 'data/4.3/datacite-example-video-v4.json', - 'data/4.3/datacite-example-dataset-v4.json', - 'data/4.3/datacite-example-Box_dateCollected_DataCollector-v4.json', - 'data/4.3/datacite-example-ResourceTypeGeneral_Collection-v4.json', - 'data/4.3/datacite-example-HasMetadata-v4.json', - 'data/4.3/datacite-example-workflow-v4.json', - 'data/4.3/datacite-example-GeoLocation-v4.json', - 'data/4.3/datacite-example-relationTypeIsIdenticalTo-v4.json', - 'data/4.3/datacite-example-software-v4.json', - 'data/4.3/datacite-example-affiliation-v4.json', - 'data/4.3/datacite-example-ancientdates-v4.json', - 'data/datacite-v4.3-full-example.json', + "data/4.3/datacite-example-polygon-v4.json", + "data/4.3/datacite-example-fundingReference-v4.json", + "data/4.3/datacite-example-ResearchGroup_Methods-v4.json", + "data/4.3/datacite-example-complicated-v4.json", + "data/4.3/datacite-example-datapaper-v4.json", + "data/4.3/datacite-example-video-v4.json", + "data/4.3/datacite-example-dataset-v4.json", + "data/4.3/datacite-example-Box_dateCollected_DataCollector-v4.json", + "data/4.3/datacite-example-ResourceTypeGeneral_Collection-v4.json", + "data/4.3/datacite-example-HasMetadata-v4.json", + "data/4.3/datacite-example-workflow-v4.json", + "data/4.3/datacite-example-GeoLocation-v4.json", + "data/4.3/datacite-example-relationTypeIsIdenticalTo-v4.json", + "data/4.3/datacite-example-software-v4.json", + "data/4.3/datacite-example-affiliation-v4.json", + "data/4.3/datacite-example-ancientdates-v4.json", + "data/datacite-v4.3-full-example.json", ] diff --git a/tests/test_client.py b/tests/test_client.py index a0ede87..0586833 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,11 +10,11 @@ """Test client.""" +import socket import ssl import pytest import responses -import socket from helpers import APIURL, get_client from mock import patch from requests import ConnectionError @@ -56,6 +56,7 @@ def test_ssl_error(requests): with pytest.raises(DataCiteHttpError): c.doi_get("10.1234/foo.bar") + # Haven't gotten timeout to work correctly with responses # Commenting out until someone can fix # @responses.activate diff --git a/tests/test_doi_get.py b/tests/test_doi_get.py index f94241f..e8ba9d1 100644 --- a/tests/test_doi_get.py +++ b/tests/test_doi_get.py @@ -14,9 +14,14 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteForbiddenError, DataCiteGoneError, \ - DataCiteNoContentError, DataCiteNotFoundError, DataCiteServerError, \ - DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteForbiddenError, + DataCiteGoneError, + DataCiteNoContentError, + DataCiteNotFoundError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate diff --git a/tests/test_doi_post.py b/tests/test_doi_post.py index 669c179..c4b9ccd 100644 --- a/tests/test_doi_post.py +++ b/tests/test_doi_post.py @@ -14,9 +14,13 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteBadRequestError, DataCiteForbiddenError, \ - DataCitePreconditionError, DataCiteServerError, \ - DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteBadRequestError, + DataCiteForbiddenError, + DataCitePreconditionError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate @@ -34,8 +38,9 @@ def test_doi_post_200(): d = get_client() assert "CREATED" == d.doi_post(doi, url) - assert responses.calls[0].request.headers['content-type'] == \ - "text/plain;charset=UTF-8" + assert ( + responses.calls[0].request.headers["content-type"] == "text/plain;charset=UTF-8" + ) expected_response = "doi={0}\r\nurl={1}".format(doi, url).encode("utf8") assert responses.calls[0].request.body == expected_response diff --git a/tests/test_example.py b/tests/test_example.py index 600c03e..0a7266f 100644 --- a/tests/test_example.py +++ b/tests/test_example.py @@ -58,7 +58,7 @@ def test_example(): responses.GET, "{0}media/{1}".format(APIURL, doi), body="application/json=http://example.org/test-doi/json/\r\n" - "application/xml=http://example.org/test-doi/xml/\r\n", + "application/xml=http://example.org/test-doi/xml/\r\n", status=200, ) @@ -80,5 +80,6 @@ def test_example(): ) import example.full + assert example.full.location == url assert example.full.doc == "..." diff --git a/tests/test_media_get.py b/tests/test_media_get.py index 5d96cc8..4347b06 100644 --- a/tests/test_media_get.py +++ b/tests/test_media_get.py @@ -14,8 +14,12 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteForbiddenError, DataCiteNotFoundError, \ - DataCiteServerError, DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteForbiddenError, + DataCiteNotFoundError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate @@ -25,14 +29,15 @@ def test_media_get_200(): responses.GET, "{0}media/10.1234/1".format(APIURL), body="application/json=http://example.org/json\r\n" - "text/plain=http://example.org/text\r\n", + "text/plain=http://example.org/text\r\n", status=200, ) d = get_client() - assert d.media_get("10.1234/1") == \ - {u'application/json': u'http://example.org/json', - u'text/plain': u'http://example.org/text'} + assert d.media_get("10.1234/1") == { + "application/json": "http://example.org/json", + "text/plain": "http://example.org/text", + } @responses.activate diff --git a/tests/test_media_post.py b/tests/test_media_post.py index c41e0b5..4463829 100644 --- a/tests/test_media_post.py +++ b/tests/test_media_post.py @@ -14,8 +14,12 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteBadRequestError, DataCiteForbiddenError, \ - DataCiteServerError, DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteBadRequestError, + DataCiteForbiddenError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate @@ -29,16 +33,17 @@ def test_media_post_200(): ) d = get_client() - assert "OK" == d.media_post("10.1234/1", { - 'text/plain': 'http://example.org/text', - 'application/json': 'http://example.org/json', - }) - assert responses.calls[0].request.headers['content-type'] == \ - "text/plain;charset=UTF-8" - lines = filter( - lambda x: x, - responses.calls[0].request.body.splitlines() + assert "OK" == d.media_post( + "10.1234/1", + { + "text/plain": "http://example.org/text", + "application/json": "http://example.org/json", + }, ) + assert ( + responses.calls[0].request.headers["content-type"] == "text/plain;charset=UTF-8" + ) + lines = filter(lambda x: x, responses.calls[0].request.body.splitlines()) assert len(list(lines)) == 2 @@ -54,7 +59,7 @@ def test_media_post_400(): d = get_client() with pytest.raises(DataCiteBadRequestError): - d.media_post("10.1234/1", {'text/plain': 'http://invaliddomain.org'}) + d.media_post("10.1234/1", {"text/plain": "http://invaliddomain.org"}) @responses.activate @@ -69,7 +74,7 @@ def test_media_post_401(): d = get_client() with pytest.raises(DataCiteUnauthorizedError): - d.media_post("10.1234/1", {'text/plain': 'http://example.org'}) + d.media_post("10.1234/1", {"text/plain": "http://example.org"}) @responses.activate @@ -84,7 +89,7 @@ def test_media_post_403(): d = get_client() with pytest.raises(DataCiteForbiddenError): - d.media_post("10.1234/1", {'text/plain': 'http://example.org'}) + d.media_post("10.1234/1", {"text/plain": "http://example.org"}) @responses.activate @@ -99,4 +104,4 @@ def test_media_post_500(): d = get_client() with pytest.raises(DataCiteServerError): - d.media_post("10.1234/1", {'text/plain': 'http://example.org'}) + d.media_post("10.1234/1", {"text/plain": "http://example.org"}) diff --git a/tests/test_metadata_delete.py b/tests/test_metadata_delete.py index 1d42457..81d1b6a 100644 --- a/tests/test_metadata_delete.py +++ b/tests/test_metadata_delete.py @@ -14,8 +14,12 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteForbiddenError, DataCiteNotFoundError, \ - DataCiteServerError, DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteForbiddenError, + DataCiteNotFoundError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate diff --git a/tests/test_metadata_get.py b/tests/test_metadata_get.py index 99ffb63..92bb27d 100644 --- a/tests/test_metadata_get.py +++ b/tests/test_metadata_get.py @@ -14,8 +14,13 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteForbiddenError, DataCiteGoneError, \ - DataCiteNotFoundError, DataCiteServerError, DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteForbiddenError, + DataCiteGoneError, + DataCiteNotFoundError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate diff --git a/tests/test_metadata_post.py b/tests/test_metadata_post.py index 5a652b2..e3da7b6 100644 --- a/tests/test_metadata_post.py +++ b/tests/test_metadata_post.py @@ -14,8 +14,12 @@ import responses from helpers import APIURL, get_client -from datacite.errors import DataCiteBadRequestError, DataCiteForbiddenError, \ - DataCiteServerError, DataCiteUnauthorizedError +from datacite.errors import ( + DataCiteBadRequestError, + DataCiteForbiddenError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @responses.activate @@ -30,8 +34,10 @@ def test_metadata_post_201(): d = get_client() assert "OK" == d.metadata_post("") - assert responses.calls[0].request.headers['content-type'] == \ - "application/xml;charset=UTF-8" + assert ( + responses.calls[0].request.headers["content-type"] + == "application/xml;charset=UTF-8" + ) @responses.activate diff --git a/tests/test_rest.py b/tests/test_rest.py index 93d15a3..e18f72a 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -14,23 +14,34 @@ import pytest import responses -from helpers import RESTURL, TEST_43_JSON_FILES, get_credentials, get_rest, \ - load_json_path - -from datacite.errors import DataCiteForbiddenError, DataCiteGoneError, \ - DataCiteNoContentError, DataCiteNotFoundError, DataCiteServerError, \ - DataCiteUnauthorizedError +from helpers import ( + RESTURL, + TEST_43_JSON_FILES, + get_credentials, + get_rest, + load_json_path, +) + +from datacite.errors import ( + DataCiteForbiddenError, + DataCiteGoneError, + DataCiteNoContentError, + DataCiteNotFoundError, + DataCiteServerError, + DataCiteUnauthorizedError, +) @pytest.mark.pw def test_rest_create_draft(): username, password, prefix = get_credentials() - d = get_rest(username=username, password=password, prefix=prefix, - with_fake_url=False) + d = get_rest( + username=username, password=password, prefix=prefix, with_fake_url=False + ) doi = d.draft_doi() - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix - url = 'https://github.com/inveniosoftware/datacite' + url = "https://github.com/inveniosoftware/datacite" new_url = d.update_url(doi, url) assert new_url == url d.delete_doi(doi) @@ -39,33 +50,34 @@ def test_rest_create_draft(): @pytest.mark.pw def test_rest_create_draft_metadata(): username, password, prefix = get_credentials() - d = get_rest(username=username, password=password, prefix=prefix, - with_fake_url=False) + d = get_rest( + username=username, password=password, prefix=prefix, with_fake_url=False + ) metadata = {"titles": [{"title": "hello world", "lang": "en"}]} - doi = prefix+'/12345' + doi = prefix + "/12345" returned_doi = d.draft_doi(metadata, doi) assert returned_doi == doi - url = 'https://github.com/inveniosoftware/datacite' + url = "https://github.com/inveniosoftware/datacite" returned_metadata = d.update_doi(doi, url=url) - assert returned_metadata['url'] == url - assert returned_metadata['titles'][0]['title'] == 'hello world' + assert returned_metadata["url"] == url + assert returned_metadata["titles"][0]["title"] == "hello world" d.delete_doi(doi) @responses.activate def test_rest_create_draft_mock(): - prefix = '10.1234' - mock = 'https://github.com/inveniosoftware/datacite' - data = {"data": {"id": prefix+"/1", "attributes": {"url": mock}}} + prefix = "10.1234" + mock = "https://github.com/inveniosoftware/datacite" + data = {"data": {"id": prefix + "/1", "attributes": {"url": mock}}} responses.add( responses.POST, "{0}dois".format(RESTURL), json=data, status=201, ) - d = get_rest(username='mock', password='mock', prefix=prefix) + d = get_rest(username="mock", password="mock", prefix=prefix) doi = d.draft_doi() - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix responses.add( @@ -85,89 +97,96 @@ def test_rest_create_draft_mock(): d.delete_doi(doi) -@pytest.mark.parametrize('example_json43', TEST_43_JSON_FILES) +@pytest.mark.parametrize("example_json43", TEST_43_JSON_FILES) @pytest.mark.pw def test_rest_create_public(example_json43): """Test creating DOIs with all example metadata""" example_metadata = load_json_path(example_json43) - url = 'https://github.com/inveniosoftware/datacite' + url = "https://github.com/inveniosoftware/datacite" username, password, prefix = get_credentials() - d = get_rest(username=username, password=password, prefix=prefix, - with_fake_url=False) + d = get_rest( + username=username, password=password, prefix=prefix, with_fake_url=False + ) doi = d.public_doi(example_metadata, url) - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix - metadata = {'publisher': 'Invenio'} + metadata = {"publisher": "Invenio"} new_metadata = d.update_doi(doi, metadata) - assert new_metadata['publisher'] == 'Invenio' - url = 'https://github.com/inveniosoftware' + assert new_metadata["publisher"] == "Invenio" + url = "https://github.com/inveniosoftware" new_metadata = d.update_doi(doi, url=url) - assert new_metadata['url'] == url + assert new_metadata["url"] == url @responses.activate def test_rest_create_public_mock(): """Test creating DOI""" - prefix = '10.1234' - example_json43 = 'data/datacite-v4.3-full-example.json' + prefix = "10.1234" + example_json43 = "data/datacite-v4.3-full-example.json" example_metadata = load_json_path(example_json43) - url = 'https://github.com/inveniosoftware/datacite' - data = {"data": {"id": prefix+"/1", "attributes": {"url": url}}} + url = "https://github.com/inveniosoftware/datacite" + data = {"data": {"id": prefix + "/1", "attributes": {"url": url}}} responses.add( responses.POST, "{0}dois".format(RESTURL), json=data, status=201, ) - d = get_rest(username='mock', password='mock', prefix=prefix) + d = get_rest(username="mock", password="mock", prefix=prefix) doi = d.public_doi(example_metadata, url) - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix - url = 'https://github.com/inveniosoftware' - data = {"data": {"id": prefix+"/1", "attributes": - {"publisher": "Invenio", "url": url}}} + url = "https://github.com/inveniosoftware" + data = { + "data": { + "id": prefix + "/1", + "attributes": {"publisher": "Invenio", "url": url}, + } + } responses.add( responses.PUT, "{0}dois/{1}/1".format(RESTURL, prefix), json=data, status=200, ) - metadata = {'publisher': 'Invenio'} + metadata = {"publisher": "Invenio"} new_metadata = d.update_doi(doi, metadata) - assert new_metadata['publisher'] == 'Invenio' + assert new_metadata["publisher"] == "Invenio" new_metadata = d.update_doi(doi, url=url) - assert new_metadata['url'] == url + assert new_metadata["url"] == url @pytest.mark.pw def test_rest_create_private(): """Test creating private DOI""" - example_json43 = 'data/4.3/datacite-example-dataset-v4.json' + example_json43 = "data/4.3/datacite-example-dataset-v4.json" example_metadata = load_json_path(example_json43) - url = 'https://github.com/inveniosoftware/datacite' + url = "https://github.com/inveniosoftware/datacite" username, password, prefix = get_credentials() - d = get_rest(username=username, password=password, prefix=prefix, - with_fake_url=False) + d = get_rest( + username=username, password=password, prefix=prefix, with_fake_url=False + ) doi = d.private_doi(example_metadata, url) - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix datacite_metadata = d.get_metadata(doi) - assert datacite_metadata['state'] == 'registered' + assert datacite_metadata["state"] == "registered" new_metadata = d.show_doi(doi) - assert new_metadata['state'] == 'findable' + assert new_metadata["state"] == "findable" new_metadata = d.hide_doi(doi) - assert new_metadata['state'] == 'registered' + assert new_metadata["state"] == "registered" @responses.activate def test_rest_create_private_mock(): """Test creating private DOI""" - example_json43 = 'data/4.3/datacite-example-dataset-v4.json' + example_json43 = "data/4.3/datacite-example-dataset-v4.json" example_metadata = load_json_path(example_json43) - prefix = '10.1234' - url = 'https://github.com/inveniosoftware/datacite' - data = {"data": {"id": prefix+"/1", "attributes": - {"state": "registered", "url": url}}} + prefix = "10.1234" + url = "https://github.com/inveniosoftware/datacite" + data = { + "data": {"id": prefix + "/1", "attributes": {"state": "registered", "url": url}} + } responses.add( responses.POST, "{0}dois".format(RESTURL), @@ -180,14 +199,15 @@ def test_rest_create_private_mock(): json=data, status=200, ) - d = get_rest(username='mock', password='mock', prefix=prefix) + d = get_rest(username="mock", password="mock", prefix=prefix) doi = d.private_doi(example_metadata, url) - datacite_prefix = doi.split('/')[0] + datacite_prefix = doi.split("/")[0] assert datacite_prefix == prefix datacite_metadata = d.get_metadata(doi) - assert datacite_metadata['state'] == 'registered' - data = {"data": {"id": prefix+"/1", "attributes": - {"state": "findable", "url": url}}} + assert datacite_metadata["state"] == "registered" + data = { + "data": {"id": prefix + "/1", "attributes": {"state": "findable", "url": url}} + } responses.add( responses.PUT, "{0}dois/{1}/1".format(RESTURL, prefix), @@ -195,7 +215,7 @@ def test_rest_create_private_mock(): status=200, ) new_metadata = d.show_doi(doi) - assert new_metadata['state'] == 'findable' + assert new_metadata["state"] == "findable" @responses.activate diff --git a/tests/test_schema31.py b/tests/test_schema31.py index f6bb929..220673b 100644 --- a/tests/test_schema31.py +++ b/tests/test_schema31.py @@ -10,8 +10,9 @@ """Tests for format transformations.""" -import pytest import xml.etree.ElementTree as ET + +import pytest from lxml import etree from datacite.schema31 import dump_etree, tostring, validate @@ -22,8 +23,8 @@ def test_rules(): """Test rules.""" rules = Rules() - rules.rule('a')(lambda x: 'a') - pytest.raises(ValueError, rules.rule('a'), lambda x: 'b') + rules.rule("a")(lambda x: "a") + pytest.raises(ValueError, rules.rule("a"), lambda x: "b") def test_example_json_validates(example_json): @@ -33,356 +34,406 @@ def test_example_json_validates(example_json): def test_json_to_xml(example_xml_file, example_json, xsd31): """Test that example XML converts to example JSON.""" - xsd31.assertValid(etree.XML(example_xml_file.encode('utf8'))) - xsd31.assertValid(etree.XML(tostring(example_json).encode('utf8'))) + xsd31.assertValid(etree.XML(example_xml_file.encode("utf8"))) + xsd31.assertValid(etree.XML(tostring(example_json).encode("utf8"))) def test_identifier(): """Test that example XML converts to example JSON.""" - tree = dump_etree({ - 'identifier': { - 'identifierType': 'DOI', - 'identifier': '10.1234/foo.bar', + tree = dump_etree( + { + "identifier": { + "identifierType": "DOI", + "identifier": "10.1234/foo.bar", + } } - }) - elem = tree.xpath('/resource/identifier')[0] - assert elem.text == '10.1234/foo.bar' - assert elem.get('identifierType') == 'DOI' + ) + elem = tree.xpath("/resource/identifier")[0] + assert elem.text == "10.1234/foo.bar" + assert elem.get("identifierType") == "DOI" def test_creators(): """Test creators.""" - pytest.raises(TypeError, dump_etree, {'creators': {'invalid': 'data'}}) - - tree = dump_etree({'creators': []}) - assert len(tree.xpath('/resource/creators')) == 0 - - tree = dump_etree({'creators': [{ - 'creatorName': 'Smith, John' - }]}) - assert len(tree.xpath('/resource/creators/creator')) == 1 - assert len(tree.xpath('/resource/creators/creator/creatorName')) == 1 - assert len(tree.xpath('/resource/creators/creator/nameIdentifier')) == 0 - assert len(tree.xpath('/resource/creators/creator/affiliation')) == 0 - - tree = dump_etree({'creators': [{ - 'creatorName': 'Smith, John', - 'affiliation': 'CERN', - 'nameIdentifier': { - 'nameIdentifier': '1234', - 'schemeURI': 'http://orcid.org', - 'nameIdentifierScheme': 'orcid', - }, - }]}) - assert len(tree.xpath('/resource/creators/creator/creatorName')) == 1 - assert len(tree.xpath('/resource/creators/creator/nameIdentifier')) == 1 - assert len(tree.xpath('/resource/creators/creator/affiliation')) == 1 + pytest.raises(TypeError, dump_etree, {"creators": {"invalid": "data"}}) + + tree = dump_etree({"creators": []}) + assert len(tree.xpath("/resource/creators")) == 0 + + tree = dump_etree({"creators": [{"creatorName": "Smith, John"}]}) + assert len(tree.xpath("/resource/creators/creator")) == 1 + assert len(tree.xpath("/resource/creators/creator/creatorName")) == 1 + assert len(tree.xpath("/resource/creators/creator/nameIdentifier")) == 0 + assert len(tree.xpath("/resource/creators/creator/affiliation")) == 0 + + tree = dump_etree( + { + "creators": [ + { + "creatorName": "Smith, John", + "affiliation": "CERN", + "nameIdentifier": { + "nameIdentifier": "1234", + "schemeURI": "http://orcid.org", + "nameIdentifierScheme": "orcid", + }, + } + ] + } + ) + assert len(tree.xpath("/resource/creators/creator/creatorName")) == 1 + assert len(tree.xpath("/resource/creators/creator/nameIdentifier")) == 1 + assert len(tree.xpath("/resource/creators/creator/affiliation")) == 1 def test_titles(): """Test titles.""" - pytest.raises(TypeError, dump_etree, {'titles': {'invalid': 'data'}}) + pytest.raises(TypeError, dump_etree, {"titles": {"invalid": "data"}}) - tree = dump_etree({'titles': []}) - assert len(tree.xpath('/resource/titles')) == 0 + tree = dump_etree({"titles": []}) + assert len(tree.xpath("/resource/titles")) == 0 - tree = dump_etree({'titles': [ - {'title': 'Test'} - ]}) - assert len(tree.xpath('/resource/titles')) == 1 - assert len(tree.xpath('/resource/titles/title')) == 1 + tree = dump_etree({"titles": [{"title": "Test"}]}) + assert len(tree.xpath("/resource/titles")) == 1 + assert len(tree.xpath("/resource/titles/title")) == 1 - elem = dump_etree({'titles': [ - {'title': 'Test', 'titleType': 'Subtitle'} - ]}).xpath('/resource/titles/title')[0] - assert elem.text == 'Test' - assert elem.get('titleType') == 'Subtitle' + elem = dump_etree({"titles": [{"title": "Test", "titleType": "Subtitle"}]}).xpath( + "/resource/titles/title" + )[0] + assert elem.text == "Test" + assert elem.get("titleType") == "Subtitle" - elem = dump_etree({'titles': [ - {'title': 'Test', 'lang': 'en'} - ]}).xpath('/resource/titles/title')[0] - assert elem.get('{xml}lang') == 'en' + elem = dump_etree({"titles": [{"title": "Test", "lang": "en"}]}).xpath( + "/resource/titles/title" + )[0] + assert elem.get("{xml}lang") == "en" def test_publisher(): """Test publisher.""" - tree = dump_etree({'publisher': 'test'}) - assert tree.xpath('/resource/publisher')[0].text == 'test' + tree = dump_etree({"publisher": "test"}) + assert tree.xpath("/resource/publisher")[0].text == "test" - tree = dump_etree({'publisher': ''}) - assert len(tree.xpath('/resource/publisher')) == 0 + tree = dump_etree({"publisher": ""}) + assert len(tree.xpath("/resource/publisher")) == 0 def test_publicationyear(): """Test publisher.""" - tree = dump_etree({'publicationYear': 2002}) - assert tree.xpath('/resource/publicationYear')[0].text == '2002' + tree = dump_etree({"publicationYear": 2002}) + assert tree.xpath("/resource/publicationYear")[0].text == "2002" - tree = dump_etree({'publicationYear': None}) - assert len(tree.xpath('/resource/publicationYear')) == 0 + tree = dump_etree({"publicationYear": None}) + assert len(tree.xpath("/resource/publicationYear")) == 0 def test_subjects(): """Test creators.""" - pytest.raises(TypeError, dump_etree, {'subjects': {'invalid': 'data'}}) + pytest.raises(TypeError, dump_etree, {"subjects": {"invalid": "data"}}) - tree = dump_etree({'subjects': []}) - assert len(tree.xpath('/resource/subjects')) == 0 + tree = dump_etree({"subjects": []}) + assert len(tree.xpath("/resource/subjects")) == 0 - tree = dump_etree({'subjects': [{ - 'subject': 'test' - }]}) - assert len(tree.xpath('/resource/subjects/subject')) == 1 + tree = dump_etree({"subjects": [{"subject": "test"}]}) + assert len(tree.xpath("/resource/subjects/subject")) == 1 - elem = dump_etree({'subjects': [{ - 'subject': 'test', - 'subjectScheme': 'dewey', - 'schemeURI': 'dewey-uri', - }]}).xpath('/resource/subjects/subject')[0] - assert elem.get('subjectScheme') == 'dewey' - assert elem.get('schemeURI') == 'dewey-uri' + elem = dump_etree( + { + "subjects": [ + { + "subject": "test", + "subjectScheme": "dewey", + "schemeURI": "dewey-uri", + } + ] + } + ).xpath("/resource/subjects/subject")[0] + assert elem.get("subjectScheme") == "dewey" + assert elem.get("schemeURI") == "dewey-uri" def test_contributors(): """Test creators.""" - pytest.raises(TypeError, dump_etree, {'contributors': {'invalid': 'data'}}) - - tree = dump_etree({'contributors': []}) - assert len(tree.xpath('/resource/contributors')) == 0 - - tree = dump_etree({'contributors': [{ - 'contributorName': 'Smith, John', - 'contributorType': 'Funder', - }]}) - assert len(tree.xpath( - '/resource/contributors/contributor')) == 1 - assert len(tree.xpath( - '/resource/contributors/contributor/contributorName')) == 1 - assert len(tree.xpath( - '/resource/contributors/contributor/nameIdentifier')) == 0 - assert len(tree.xpath( - '/resource/contributors/contributor/affiliation')) == 0 - - tree = dump_etree({'contributors': [{ - 'contributorName': 'Smith, John', - 'contributorType': 'Funder', - 'affiliation': 'CERN', - 'nameIdentifier': { - 'nameIdentifier': '1234', - 'schemeURI': 'http://orcid.org', - 'nameIdentifierScheme': 'orcid', - }, - }]}) - assert len(tree.xpath( - '/resource/contributors/contributor/contributorName')) == 1 - assert len(tree.xpath( - '/resource/contributors/contributor/nameIdentifier')) == 1 - assert len(tree.xpath( - '/resource/contributors/contributor/affiliation')) == 1 + pytest.raises(TypeError, dump_etree, {"contributors": {"invalid": "data"}}) + + tree = dump_etree({"contributors": []}) + assert len(tree.xpath("/resource/contributors")) == 0 + + tree = dump_etree( + { + "contributors": [ + { + "contributorName": "Smith, John", + "contributorType": "Funder", + } + ] + } + ) + assert len(tree.xpath("/resource/contributors/contributor")) == 1 + assert len(tree.xpath("/resource/contributors/contributor/contributorName")) == 1 + assert len(tree.xpath("/resource/contributors/contributor/nameIdentifier")) == 0 + assert len(tree.xpath("/resource/contributors/contributor/affiliation")) == 0 + + tree = dump_etree( + { + "contributors": [ + { + "contributorName": "Smith, John", + "contributorType": "Funder", + "affiliation": "CERN", + "nameIdentifier": { + "nameIdentifier": "1234", + "schemeURI": "http://orcid.org", + "nameIdentifierScheme": "orcid", + }, + } + ] + } + ) + assert len(tree.xpath("/resource/contributors/contributor/contributorName")) == 1 + assert len(tree.xpath("/resource/contributors/contributor/nameIdentifier")) == 1 + assert len(tree.xpath("/resource/contributors/contributor/affiliation")) == 1 def test_dates(): """Test publisher.""" - tree = dump_etree({'dates': []}) - assert len(tree.xpath('/resource/dates')) == 0 + tree = dump_etree({"dates": []}) + assert len(tree.xpath("/resource/dates")) == 0 - pytest.raises(KeyError, dump_etree, {'dates': [{'date': '2011-01-01'}]}) + pytest.raises(KeyError, dump_etree, {"dates": [{"date": "2011-01-01"}]}) - elem = dump_etree({'dates': [ - {'date': '2011-01-01', 'dateType': 'Accepted'} - ]}).xpath('/resource/dates/date')[0] - assert elem.text == '2011-01-01' - assert elem.get('dateType') == 'Accepted' + elem = dump_etree( + {"dates": [{"date": "2011-01-01", "dateType": "Accepted"}]} + ).xpath("/resource/dates/date")[0] + assert elem.text == "2011-01-01" + assert elem.get("dateType") == "Accepted" def test_language(): """Test language.""" - tree = dump_etree({'language': 'en'}) - assert tree.xpath('/resource/language')[0].text == 'en' + tree = dump_etree({"language": "en"}) + assert tree.xpath("/resource/language")[0].text == "en" - tree = dump_etree({'language': ''}) - assert len(tree.xpath('/resource/language')) == 0 + tree = dump_etree({"language": ""}) + assert len(tree.xpath("/resource/language")) == 0 def test_resourcetype(): """Test resource type.""" - tree = dump_etree({'resourceType': {}}) - assert len(tree.xpath('/resource/resourceType')) == 0 + tree = dump_etree({"resourceType": {}}) + assert len(tree.xpath("/resource/resourceType")) == 0 - elem = dump_etree({'resourceType': { - 'resourceTypeGeneral': 'Software' - }}).xpath('/resource/resourceType')[0] - assert elem.get('resourceTypeGeneral') == 'Software' + elem = dump_etree({"resourceType": {"resourceTypeGeneral": "Software"}}).xpath( + "/resource/resourceType" + )[0] + assert elem.get("resourceTypeGeneral") == "Software" assert elem.text is None - pytest.raises(KeyError, dump_etree, {'resourceType': { - 'resourceType': 'Science Software' - }}) + pytest.raises( + KeyError, dump_etree, {"resourceType": {"resourceType": "Science Software"}} + ) - elem = dump_etree({'resourceType': { - 'resourceTypeGeneral': 'Software', - 'resourceType': 'Science Software' - }}).xpath('/resource/resourceType')[0] - assert elem.get('resourceTypeGeneral') == 'Software' - assert elem.text == 'Science Software' + elem = dump_etree( + { + "resourceType": { + "resourceTypeGeneral": "Software", + "resourceType": "Science Software", + } + } + ).xpath("/resource/resourceType")[0] + assert elem.get("resourceTypeGeneral") == "Software" + assert elem.text == "Science Software" def test_alternateidentifiers(): """Test publisher.""" - pytest.raises(TypeError, dump_etree, {'alternateIdentifiers': { - 'invalid': 'data' - }}) + pytest.raises(TypeError, dump_etree, {"alternateIdentifiers": {"invalid": "data"}}) - tree = dump_etree({'alternateIdentifiers': []}) - assert len(tree.xpath('/resource/alternateIdentifiers')) == 0 + tree = dump_etree({"alternateIdentifiers": []}) + assert len(tree.xpath("/resource/alternateIdentifiers")) == 0 - elem = dump_etree({'alternateIdentifiers': [ + elem = dump_etree( { - 'alternateIdentifier': '10.1234/foo', - 'alternateIdentifierType': 'DOI', - }, - ]}).xpath('/resource/alternateIdentifiers/alternateIdentifier')[0] - assert elem.get('alternateIdentifierType') == 'DOI' - assert elem.text == '10.1234/foo' + "alternateIdentifiers": [ + { + "alternateIdentifier": "10.1234/foo", + "alternateIdentifierType": "DOI", + }, + ] + } + ).xpath("/resource/alternateIdentifiers/alternateIdentifier")[0] + assert elem.get("alternateIdentifierType") == "DOI" + assert elem.text == "10.1234/foo" def test_relatedidentifiers(): """Test publisher.""" - tree = dump_etree({'relatedIdentifiers': []}) - assert len(tree.xpath('/resource/relatedIdentifiers')) == 0 + tree = dump_etree({"relatedIdentifiers": []}) + assert len(tree.xpath("/resource/relatedIdentifiers")) == 0 - elem = dump_etree({'relatedIdentifiers': [ + elem = dump_etree( { - 'relatedIdentifier': '10.1234/foo', - 'relatedIdentifierType': 'DOI', - 'relationType': 'Cites' - }, - ]}).xpath('/resource/relatedIdentifiers/relatedIdentifier')[0] - assert elem.get('relatedIdentifierType') == 'DOI' - assert elem.get('relationType') == 'Cites' - assert elem.text == '10.1234/foo' - - elem = dump_etree({'relatedIdentifiers': [ + "relatedIdentifiers": [ + { + "relatedIdentifier": "10.1234/foo", + "relatedIdentifierType": "DOI", + "relationType": "Cites", + }, + ] + } + ).xpath("/resource/relatedIdentifiers/relatedIdentifier")[0] + assert elem.get("relatedIdentifierType") == "DOI" + assert elem.get("relationType") == "Cites" + assert elem.text == "10.1234/foo" + + elem = dump_etree( { - 'relatedIdentifier': '10.1234/foo', - 'relatedIdentifierType': 'DOI', - 'relationType': 'HasMetadata', - 'relatedMetadataScheme': 'MARC21', - 'schemeURI': 'http://loc.gov', - 'schemeType': 'XSD', - }, - ]}).xpath('/resource/relatedIdentifiers/relatedIdentifier')[0] - assert elem.get('relatedMetadataScheme') == 'MARC21' - assert elem.get('schemeURI') == 'http://loc.gov' - assert elem.get('schemeType') == 'XSD' + "relatedIdentifiers": [ + { + "relatedIdentifier": "10.1234/foo", + "relatedIdentifierType": "DOI", + "relationType": "HasMetadata", + "relatedMetadataScheme": "MARC21", + "schemeURI": "http://loc.gov", + "schemeType": "XSD", + }, + ] + } + ).xpath("/resource/relatedIdentifiers/relatedIdentifier")[0] + assert elem.get("relatedMetadataScheme") == "MARC21" + assert elem.get("schemeURI") == "http://loc.gov" + assert elem.get("schemeType") == "XSD" def test_sizes(): """Test sizes.""" - tree = dump_etree({'sizes': []}) - assert len(tree.xpath('/resource/sizes')) == 0 + tree = dump_etree({"sizes": []}) + assert len(tree.xpath("/resource/sizes")) == 0 - elem = dump_etree({'sizes': ['123']}).xpath('/resource/sizes/size')[0] - assert elem.text == '123' + elem = dump_etree({"sizes": ["123"]}).xpath("/resource/sizes/size")[0] + assert elem.text == "123" def test_formats(): """Test formats.""" - tree = dump_etree({'formats': []}) - assert len(tree.xpath('/resource/formats')) == 0 + tree = dump_etree({"formats": []}) + assert len(tree.xpath("/resource/formats")) == 0 - elem = dump_etree( - {'formats': ['abc']}).xpath('/resource/formats/format')[0] - assert elem.text == 'abc' + elem = dump_etree({"formats": ["abc"]}).xpath("/resource/formats/format")[0] + assert elem.text == "abc" def test_version(): """Test formats.""" - tree = dump_etree({'version': ''}) - assert len(tree.xpath('/resource/version')) == 0 + tree = dump_etree({"version": ""}) + assert len(tree.xpath("/resource/version")) == 0 - elem = dump_etree( - {'version': 'v3.1'}).xpath('/resource/version')[0] - assert elem.text == 'v3.1' + elem = dump_etree({"version": "v3.1"}).xpath("/resource/version")[0] + assert elem.text == "v3.1" def test_rights(): """Test publisher.""" - tree = dump_etree({'rightsList': []}) - assert len(tree.xpath('/resource/rightsList')) == 0 + tree = dump_etree({"rightsList": []}) + assert len(tree.xpath("/resource/rightsList")) == 0 - elem = dump_etree({'rightsList': [ + elem = dump_etree( { - 'rights': 'CC', - 'rightsURI': 'http://cc.org', - }, - ]}).xpath('/resource/rightsList/rights')[0] - assert elem.get('rightsURI') == 'http://cc.org' - assert elem.text == 'CC' + "rightsList": [ + { + "rights": "CC", + "rightsURI": "http://cc.org", + }, + ] + } + ).xpath("/resource/rightsList/rights")[0] + assert elem.get("rightsURI") == "http://cc.org" + assert elem.text == "CC" def test_descriptions(): """Test publisher.""" - tree = dump_etree({'descriptions': []}) - assert len(tree.xpath('/resource/descriptions')) == 0 + tree = dump_etree({"descriptions": []}) + assert len(tree.xpath("/resource/descriptions")) == 0 - elem = dump_etree({'descriptions': [ + elem = dump_etree( { - 'description': 'Test', - 'descriptionType': 'Abstract', - }, - ]}).xpath('/resource/descriptions/description')[0] - assert elem.get('descriptionType') == 'Abstract' - assert elem.text == 'Test' + "descriptions": [ + { + "description": "Test", + "descriptionType": "Abstract", + }, + ] + } + ).xpath("/resource/descriptions/description")[0] + assert elem.get("descriptionType") == "Abstract" + assert elem.text == "Test" def test_geolocations(): """Test publisher.""" - tree = dump_etree({'geoLocations': []}) - assert len(tree.xpath('/resource/geoLocations')) == 0 - - elem = dump_etree({'geoLocations': [{ - 'geoLocationPoint': '31 67', - 'geoLocationBox': '31 67 32 68', - 'geoLocationPlace': 'Atlantic Ocean', - }, ]}).xpath('/resource/geoLocations/geoLocation')[0] - point = elem.xpath('geoLocationPoint')[0] - assert point.text == '31 67' - box = elem.xpath('geoLocationBox')[0] - assert box.text == '31 67 32 68' - place = elem.xpath('geoLocationPlace')[0] - assert place.text == 'Atlantic Ocean' + tree = dump_etree({"geoLocations": []}) + assert len(tree.xpath("/resource/geoLocations")) == 0 + + elem = dump_etree( + { + "geoLocations": [ + { + "geoLocationPoint": "31 67", + "geoLocationBox": "31 67 32 68", + "geoLocationPlace": "Atlantic Ocean", + }, + ] + } + ).xpath("/resource/geoLocations/geoLocation")[0] + point = elem.xpath("geoLocationPoint")[0] + assert point.text == "31 67" + box = elem.xpath("geoLocationBox")[0] + assert box.text == "31 67 32 68" + place = elem.xpath("geoLocationPlace")[0] + assert place.text == "Atlantic Ocean" def test_minimal_xsd(xsd31): """Test that example XML converts to example JSON.""" - xsd31.assertValid(etree.XML(tostring({ - 'identifier': { - 'identifierType': 'DOI', - 'identifier': '10.1234/foo.bar', - }, - 'creators': [ - {'creatorName': 'Nielsen, Lars Holm', }, - { - 'creatorName': 'Nielsen, Lars Holm', - 'nameIdentifier': { - 'nameIdentifier': '1234', - 'schemeURI': 'http://orcid.org', - 'nameIdentifierScheme': 'ORCID', + xsd31.assertValid( + etree.XML( + tostring( + { + "identifier": { + "identifierType": "DOI", + "identifier": "10.1234/foo.bar", + }, + "creators": [ + { + "creatorName": "Nielsen, Lars Holm", + }, + { + "creatorName": "Nielsen, Lars Holm", + "nameIdentifier": { + "nameIdentifier": "1234", + "schemeURI": "http://orcid.org", + "nameIdentifierScheme": "ORCID", + }, + }, + ], + "titles": [ + { + "title": "Minimal Test Case", + } + ], + "publisher": "Invenio Software", + "publicationYear": "2016", } - } - ], - 'titles': [ - {'title': 'Minimal Test Case', } - ], - 'publisher': 'Invenio Software', - 'publicationYear': '2016', - }).encode('utf8'))) + ).encode("utf8") + ) + ) def test_minimal_xml(xsd31): """Test minimal xml.""" from lxml import etree + xml = """