diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 3ebe023..2a9b0bb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -14,7 +14,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
- python-version: [3.6.8, 3.7, 3.8, 3.9]
+ python-version: [3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
diff --git a/bdrxml/mods.py b/bdrxml/mods.py
index a5c9290..4daa10f 100644
--- a/bdrxml/mods.py
+++ b/bdrxml/mods.py
@@ -1,16 +1,36 @@
import re
import unicodedata
+
+from .darwincore import get_schema_validation_errors
+from eulxml import xmlmap
from eulxml.xmlmap import StringField as SF, SchemaField, NodeListField, NodeField
+
#import everything from eulxml.xmlmap.mods because clients have to use a lot of
# those classes, and we're just overriding a few of them here.
from eulxml.xmlmap.mods import *
-from .darwincore import get_schema_validation_errors
+from eulxml.xmlmap.mods import BaseMods as EulXmlBaseMods
+from eulxml.xmlmap.mods import Common, Date, Note, TitleInfo
+from eulxml.xmlmap.mods import Genre as EulXmlGenre
+from eulxml.xmlmap.mods import Language as EulXmlLanguage
+from eulxml.xmlmap.mods import LanguageTerm as EulXmlLanguageTerm
+from eulxml.xmlmap.mods import Location as EulXmlLocation
+from eulxml.xmlmap.mods import Name as EulXmlName
+from eulxml.xmlmap.mods import OriginInfo as EulXmlOriginInfo
+from eulxml.xmlmap.mods import PartDetail as EulXmlPartDetail
+from eulxml.xmlmap.mods import PhysicalDescription as EulXmlPhysicalDescription
+from eulxml.xmlmap.mods import RecordInfo as EulXmlRecordInfo
+from eulxml.xmlmap.mods import RelatedItem as EulXmlRelatedItem
+from eulxml.xmlmap.mods import Role as EulXmlRole
+from eulxml.xmlmap.mods import Subject as EulXmlSubject
+
XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'
XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance'
-XSI_LOCATION = 'http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-7.xsd'
+# XSI_LOCATION = 'http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-7.xsd'
+XSI_LOCATION = 'http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-8.xsd'
MODSv35_SCHEMA = "http://www.loc.gov/standards/mods/v3/mods-3-5.xsd"
MODSv37_SCHEMA = "http://www.loc.gov/standards/mods/v3/mods-3-7.xsd"
+MODSv38_SCHEMA = "http://www.loc.gov/standards/mods/v3/mods-3-8.xsd"
FAST = 'http://id.worldcat.org/fast'
@@ -22,12 +42,14 @@ class CommonField(Common):
text = SF('text()')
-class PartDetail(PartDetail):
+class PartDetail(EulXmlPartDetail):
caption = SF('mods:caption')
-class Part(Part):
+
+class Part(EulXmlPartDetail):
details = NodeListField('mods:detail', PartDetail)
+
class PlaceTerm(CommonField):
ROOT_NAME = 'placeTerm'
type = SF('@type')
@@ -37,21 +59,23 @@ class Place(Common):
ROOT_NAME = 'place'
place_terms = NodeListField('mods:placeTerm', PlaceTerm)
-class OriginInfo(OriginInfo):
+
+class OriginInfo(EulXmlOriginInfo):
label = SF('@displayLabel')
places = NodeListField('mods:place', Place)
-class Collection(RelatedItem):
+class Collection(EulXmlRelatedItem):
name = SF('mods:titleInfo/mods:title')
id = SF('mods:identifier[@type="COLID"]')
-class LanguageTerm(LanguageTerm):
+class LanguageTerm(EulXmlLanguageTerm):
authority_uri = SF('@authorityURI')
value_uri = SF('@valueURI')
-class Language(Language):
+
+class Language(EulXmlLanguage):
terms = NodeListField('mods:languageTerm', LanguageTerm)
@@ -60,7 +84,7 @@ class PhysicalDescriptionForm(CommonField):
type = SF('@type')
-class PhysicalDescription(PhysicalDescription):
+class PhysicalDescription(EulXmlPhysicalDescription):
digital_origin = SF('mods:digitalOrigin')
note = SF('mods:note')
forms = NodeListField('mods:form', PhysicalDescriptionForm)
@@ -76,17 +100,19 @@ class SubLocation(Common):
script = SF('@script')
transliteration = SF('@transliteration')
+
class CopyInformation(Common):
ROOT_NAME = 'copyInformation'
notes = NodeListField('mods:note', Note)
sublocations = NodeListField('mods:subLocation', SubLocation)
+
class HoldingSimple(Common):
ROOT_NAME = 'holdingSimple'
copy_information = NodeListField('mods:copyInformation', CopyInformation)
-class Location(Location):
+class Location(EulXmlLocation):
physical = NodeField('mods:physicalLocation', PhysicalLocation)
holding_simple = NodeField('mods:holdingSimple', HoldingSimple)
@@ -116,7 +142,7 @@ class Topic(CommonField):
ROOT_NAME = 'topic'
-class Subject(Subject):
+class Subject(EulXmlSubject):
label = SF('@displayLabel')
authority_uri = SF('@authorityURI')
value_uri = SF('@valueURI')
@@ -136,7 +162,7 @@ class Classification(CommonField):
label = SF('@displayLabel')
-class Genre(Genre):
+class Genre(EulXmlGenre):
authority_uri = SF('@authorityURI')
value_uri = SF('@valueURI')
type = SF('@type')
@@ -156,18 +182,18 @@ class RecordContentSource(CommonField):
ROOT_NAME = 'recordContentSource'
-class RecordInfo(RecordInfo):
+class RecordInfo(EulXmlRecordInfo):
record_identifier_list = NodeListField('mods:recordIdentifier', RecordIdentifier)
record_creation_date = NodeField('mods:recordCreationDate', RecordCreationDate)
record_content_source = NodeField('mods:recordContentSource', RecordContentSource)
-class Role(Role):
+class Role(EulXmlRole):
authority_uri = SF('mods:roleTerm/@authorityURI')
value_uri = SF('mods:roleTerm/@valueURI')
-class Name(Name):
+class Name(EulXmlName):
roles = NodeListField('mods:role', Role)
authority_uri = SF('@authorityURI')
value_uri = SF('@valueURI')
@@ -182,7 +208,7 @@ class ResourceType(Common):
text = SF('text()')
-class BaseMods(BaseMods):
+class BaseMods(EulXmlBaseMods):
classifications = NodeListField('mods:classification', Classification)
origin_info = NodeField('mods:originInfo', OriginInfo)
subjects = NodeListField('mods:subject', Subject)
@@ -210,7 +236,8 @@ class Mods(BaseMods):
http://www.loc.gov/standards/mods/mods-outline.html
"""
ROOT_NAME = 'mods'
- XSD_SCHEMA = MODSv37_SCHEMA
+ # XSD_SCHEMA = MODSv37_SCHEMA
+ XSD_SCHEMA = MODSv38_SCHEMA
Common.ROOT_NAMESPACES['xlink'] = XLINK_NAMESPACE
Common.ROOT_NAMESPACES['xsi'] = XSI_NAMESPACE
xsi_schema_location = SF('@xsi:schemaLocation')
@@ -223,8 +250,9 @@ class Mods(BaseMods):
def validation_errors(self):
'''see notes on SimpleDarwinRecordSet.validation_errors()'''
- return get_schema_validation_errors(schema_name='mods-3-7.xsd', lxml_node=self.node)
-
+ #return get_schema_validation_errors(schema_name='mods-3-7.xsd', lxml_node=self.node)
+ return get_schema_validation_errors(schema_name='mods-3-8.xsd', lxml_node=self.node)
+
def make_mods():
"""Helper that returns Mods object."""
diff --git a/bdrxml/schemas/mods-3-8.xsd b/bdrxml/schemas/mods-3-8.xsd
new file mode 100644
index 0000000..72f60c6
--- /dev/null
+++ b/bdrxml/schemas/mods-3-8.xsd
@@ -0,0 +1,1774 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 0000000..10fb6eb
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1,12 @@
+## TEMP -- for development
+
+## from setup.py
+eulxml==1.1.3
+ply==3.8
+# rdflib
+
+## actual version used by workshop in production
+rdflib==6.0.0
+
+## actual version used by workshop in production -- newest version fails on some code
+lxml==4.7.1
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b8fbcf4
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,29 @@
+#
+# This file is autogenerated by pip-compile with Python 3.8
+# by the following command:
+#
+# pip-compile ./requirements.in
+#
+eulxml==1.1.3
+ # via -r ./requirements.in
+isodate==0.6.1
+ # via rdflib
+lxml==4.7.1
+ # via
+ # -r ./requirements.in
+ # eulxml
+ply==3.8
+ # via
+ # -r ./requirements.in
+ # eulxml
+pyparsing==3.1.1
+ # via rdflib
+rdflib==6.0.0
+ # via -r ./requirements.in
+six==1.16.0
+ # via
+ # eulxml
+ # isodate
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/setup.py b/setup.py
index 125dad7..8010727 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
setup(name='bdrxml',
- version='1.4',
+ version='1.5',
url='https://github.com/Brown-University-Library/bdrxml',
author='Brown University Library',
author_email='bdr@brown.edu',
diff --git a/test/mods_test.py b/test/mods_test.py
index c2f9932..3dce81b 100644
--- a/test/mods_test.py
+++ b/test/mods_test.py
@@ -1,5 +1,5 @@
import unittest
-from eulxml.xmlmap import load_xmlobject_from_string
+from eulxml.xmlmap import load_xmlobject_from_string
from bdrxml import mods
SAMPLE_MODS = '''
@@ -165,6 +165,13 @@
'''
+MODS_38_XML = '''
+
+
+ A Title
+
+
+'''
INVALID_MODS_XML = '''
@@ -366,6 +373,10 @@ def test_validate_mods_35(self):
loaded = load_xmlobject_from_string(MODS_35_XML, mods.Mods)
self.assertTrue(loaded.is_valid())
+ def test_validate_mods_38(self):
+ loaded = load_xmlobject_from_string(MODS_38_XML, mods.Mods)
+ self.assertTrue(loaded.is_valid())
+
def test_validate_created_mods(self):
self.mods.title = 'Poétry'
self.assertTrue(self.mods.is_valid())