diff options
| -rw-r--r-- | lesana/collection.py | 48 | ||||
| -rw-r--r-- | lesana/types.py | 11 | ||||
| -rw-r--r-- | tests/data/wrong/settings.yaml | 3 | ||||
| -rw-r--r-- | tests/test_collection.py | 24 | 
4 files changed, 64 insertions, 22 deletions
| diff --git a/lesana/collection.py b/lesana/collection.py index 4b3e964..5b7b809 100644 --- a/lesana/collection.py +++ b/lesana/collection.py @@ -92,22 +92,15 @@ class Entry(object):      def validate(self):          errors = []          valid = True -        for field in self.collection.settings['fields']: -            value = self.data.get(field['name'], None) -            t = field['type'] +        for name, field in self.collection.fields.items(): +            value = self.data.get(name, None) +            t = field.name              try: -                self.data[field['name']] = self.collection.types[t].load(value) -            except KeyError: -                errors.append( -                    { -                        'field': field['name'], -                        'error': "No such type {}".format(t), -                    } -                ) +                self.data[name] = field.load(value)              except types.LesanaValueError as e:                  errors.append(                      { -                        'field': field['name'], +                        'field': name,                          'error': e,                      }                  ) @@ -120,7 +113,7 @@ class Entry(object):                      valid = False                      errors.append(                          { -                            'field': field['name'], +                            'field': name,                              'error': 'Invalid value for list field: {}'.format(                                  value                              ), @@ -150,7 +143,6 @@ class Collection(object):      def __init__(self, directory=None, itemdir='items'):          self.basedir = directory or os.getcwd()          self.itemdir = os.path.join(self.basedir, itemdir) -        self.types = self._load_types()          try:              with open(os.path.join(self.basedir, 'settings.yaml')) as fp:                  self.settings = ruamel.yaml.load( @@ -158,6 +150,7 @@ class Collection(object):                  )          except FileNotFoundError:              self.settings = ruamel.yaml.safe_load("{}") +        self.fields = self.load_field_types()          os.makedirs(os.path.join(self.basedir, '.lesana'), exist_ok=True)          if 'lang' in self.settings:              try: @@ -176,11 +169,30 @@ class Collection(object):          self.safe = False          self.entry_class = Entry -    def _load_types(self): +    def _get_subsubclasses(self, cls): +        for c in cls.__subclasses__(): +            yield c +            yield from self._get_subsubclasses(c) + +    def load_field_types(self):          type_loaders = {} -        for t in types.LesanaType.__subclasses__(): -            type_loaders[t.name] = t() -        return type_loaders +        for t in self._get_subsubclasses(types.LesanaType): +            type_loaders[t.name] = t +        fields = {} +        for field in self.settings.get('fields', []): +            try: +                fields[field['name']] = type_loaders[field['type']]() +            except KeyError: +                # unknown fields are treated as if they were +                # (unvalidated) generic YAML to support working with +                # collections based on lesana derivatives +                logging.warning( +                    "Unknown field type %s in field %s", +                    field['type'], +                    field['name'], +                ) +                fields[field['name']] = types.LesanaYAML() +        return fields      def _index_file(self, fname, cache):          with open(os.path.join(self.itemdir, fname)) as fp: diff --git a/lesana/types.py b/lesana/types.py index 565d1b2..a252830 100644 --- a/lesana/types.py +++ b/lesana/types.py @@ -221,6 +221,17 @@ class LesanaYAML(LesanaType):          return None +class LesanaList(LesanaYAML): +    """ +    A list of other values +    """ + +    name = 'list' + +    # Temporary definition so that tests aren't broken in the current +    # commit + +  class LesanaValueError(ValueError):      """      Raised in case of validation errors. diff --git a/tests/data/wrong/settings.yaml b/tests/data/wrong/settings.yaml index 83a542b..bf79572 100644 --- a/tests/data/wrong/settings.yaml +++ b/tests/data/wrong/settings.yaml @@ -23,3 +23,6 @@ fields:        type: list        list: string        index: field +    - name: cloud +      type: cloud +      help: 'There is no cloud type' diff --git a/tests/test_collection.py b/tests/test_collection.py index 832f421..f7ddf6d 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -305,6 +305,22 @@ class testComplexCollection(unittest.TestCase):          self.assertIn('with_default', entry.yaml_data)          self.assertIn('amount: 0', entry.yaml_data) +    def test_load_field_loaders(self): +        # Check that all fields have been loaded, with the right types +        to_test = ( +            ('name', lesana.types.LesanaString), +            ('description', lesana.types.LesanaText), +            ('position', lesana.types.LesanaString), +            ('something', lesana.types.LesanaYAML), +            ('tags', lesana.types.LesanaList), +            ('keywords', lesana.types.LesanaList), +            ('exists', lesana.types.LesanaBoolean), +            ('with_default', lesana.types.LesanaString), +            ('amount', lesana.types.LesanaInt), +        ) +        for f in to_test: +            self.assertIsInstance(self.collection.fields[f[0]], f[1]) +  class testCollectionWithErrors(unittest.TestCase):      def setUp(self): @@ -320,8 +336,8 @@ class testCollectionWithErrors(unittest.TestCase):          # check that the log contains a warning.          with self.assertLogs(level=logging.WARNING) as cm:              self.collection = lesana.Collection(self.tmpdir) -        self.assertEqual(len(cm.output), 1) -        self.assertIn("Invalid language", cm.output[0]) +        self.assertEqual(len(cm.output), 2) +        self.assertIn("Invalid language", cm.output[1])          # The collection will default to english, but should still work.          self.collection.update_cache()          self.assertIsNotNone(self.collection.settings) @@ -334,7 +350,7 @@ class testCollectionWithErrors(unittest.TestCase):          self.assertIsNotNone(self.collection.settings)          self.assertIsNotNone(self.collection.stemmer)          # Fields with no "index" entry are not indexed -        self.assertEqual(len(self.collection.settings['fields']), 7) +        self.assertEqual(len(self.collection.settings['fields']), 8)          self.assertEqual(len(self.collection.indexed_fields), 3)      def test_init(self): @@ -343,7 +359,7 @@ class testCollectionWithErrors(unittest.TestCase):              self.collection.settings['name'],              "Lesana collection with certain errors",          ) -        self.assertEqual(len(self.collection.settings['fields']), 7) +        self.assertEqual(len(self.collection.settings['fields']), 8)          self.assertIsNotNone(self.collection.stemmer)          self.assertEqual(len(self.collection.indexed_fields), 3) | 
