Current Metadata: - 20 different metadata properties that can be set via workID/abbreviation (one value for each). Use setWorkId, set/get via attribute/property. - 10 different contributors that can be set (multiple values for each). Use contributors, addContributor, .composer, .composers, etc - A few other things to set/get: date, copyright, software - Search function to search for regex, etc. New Metadata (completely backward compatible): - 179 different metadata properties that can be set/get (multiple values for each). Use getItem/setItem/addItem, getItems/setItems/addItems. They can be set/get by 'uniqueName', or by nsKey (a.k.a. 'namespace:name'). There are three supported namespaces: 'dcterms' (the extended DublinCore list of properties), 'marcrel' (the MARC Relator list of contributor properties), and 'music21' for the workIds and contributors that have been supported historically in music21, but did not find a match in either 'dcterms' or 'marcrel' (e.g. 'suspectedComposer'). The list of 179 properties includes all the contributors, including many new ones from the MARC Relator namespace ('marcrel:XXX'). Contributors are not treated any differently from other properties, except in backward compatible APIs and some specific new APIs (e.g. getAllItems(skipContributors) and getAllContributorItems()). There is a tuple of 179 tuples (Metadata._STDPROPERTYDESCS) that contains all the information about each metadata property (namespace, name, valueType, uniqueName, etc), and the code looks things up in various dicts that are derived from this. This makes it easy to add new metadata properties in future without changing any code. Values are stored in the metadata dictionary by nsKey ('namespace:name'). If there is only one value for a key, it is stored directly as the value. If there is more than one, the value in the dictionary is a list of values. Setters do auto-conversion to each metadata property's preferred value type (Text, Copyright, DateSingle/etc, Contributor). Getters always return the preferred value type for a property. - You can get/set the workIds and contributors (and date and copyright) that existed in the current metadata implementation either via the new APIs or via the backward compatible APIs. New properties can only be set via the new APIs. - You can get/set personal metadata properties as well, with any name you like. - all(skipContributors=False) is a backward compatible API. getAllItems(skipContributors=False) is the new replacement. I've also added getAllContributors() to flesh out the set. Modifications made to converters: MusicXML: replaced existing metadata calls with new ones, and made one significant change: all the metadata that doesn't fit in and etc is read from/written to . Nothing is dropped on the floor. Braille writer: metadata export is done via the new APIs, so nothing that is in the score is dropped on the floor. MEI parser: is still using backward compatible metadata APIs, but I made one fix to set meta.composers instead of meta.composer when there's more than one composer. Changed one test to match. My own Humdrum parser/exporter is now using the new APIs as well. This was an interesting exercise because Humdrum has _more_ than 179 named metadata items. So the parser has a mapping from Humdrum to the list of 179, and those without mappings get added to the metadata via personal key, which looks like 'humdrum:XXX' (where XXX is the Humdrum metadata property name), and the Humdrum exporter knows to look for those, and pass them through to the exported Humdrum file. Humdrum also supports "personal metadata" (any key you like), and that gets passed through successfully as well (including to MusicXML). A good test: import Humdrum file, export to MusicXML, import from MusicXML, export back to Humdrum, and compare the metadata in the two Humdrum files. Other modifications: I had to regenerate the Corpus metadata cache. RichMetadata works (it knows about the new internals of class Metadata), but I didn't do anything else to make it more than just backward compatible. I enhanced Text a bit with two new fields: isTranslated and encodingScheme. encodingScheme means "which standard should I use to parse this string", not "how are the characters in this string encoded". I made a slight modification to Date.loadString to allow it to load strings that looked like '1979/--/--', since those are created by str(Date). This prevents an exception when Dates with missing fields are put into MusicXML and then read back. I added __eq__() APIs to some of the primitives (Text, Copyright, etc) so that my new tests could actually do equality comparison on them. To be done: - software is still just a List(str) attribute. I'm considering adding it to the list of 179 properties. - I'm considering the possibility of having multiple preferred value types for a single metadata property (for example, uniqueName='opusNumber' could have Text and int, instead of just Text). Clients who get would need to deal with either returned type. - search() is backward compatible: it doesn't search any of the new stuff. I think I will need to add a new searchItems() API that searches everything, and has behavior that otherwise matches the new metadata APIs (returning preferred value types, etc) - Make RichMetadata more than just backward compatible as well, once we have a new searchItems() API. - I'm considering more enhancements to DateXXX's string parsing to allow full back-and-forth to and from string without loss. For example ' or earlier' needs to be parsed in DateRelative. We might need a stringToDateObject() utility function that figures out what kind of DateXXX object to create, given the contents of the string.