A fork of hyde, the static site generation. Some patches will be pushed upstream.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

144 lines
4.6 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Contains classes and utilities related to meta data in hyde.
  4. """
  5. import re
  6. from hyde.model import Expando
  7. from hyde.plugin import Plugin
  8. import yaml
  9. class Metadata(Expando):
  10. """
  11. Container class for yaml meta data.
  12. """
  13. def __init__(self, data, parent=None):
  14. super(Metadata, self).__init__({})
  15. if parent:
  16. self.update(parent.__dict__)
  17. if data:
  18. self.update(data)
  19. def update(self, data):
  20. """
  21. Updates the metadata with new stuff
  22. """
  23. if isinstance(data, basestring):
  24. super(Metadata, self).update(yaml.load(data))
  25. else:
  26. super(Metadata, self).update(data)
  27. class MetaPlugin(Plugin):
  28. """
  29. Metadata plugin for hyde. Loads meta data in the following order:
  30. 1. meta.yaml: files in any folder
  31. 2. frontmatter: any text file with content enclosed within three dashes
  32. or three equals signs.
  33. Example:
  34. ---
  35. abc: def
  36. ---
  37. Supports YAML syntax.
  38. """
  39. def __init__(self, site):
  40. super(MetaPlugin, self).__init__(site)
  41. def begin_site(self):
  42. """
  43. Initialize site meta data.
  44. Go through all the nodes and resources to initialize
  45. meta data at each level.
  46. """
  47. config = self.site.config
  48. metadata = config.meta if hasattr(config, 'meta') else {}
  49. self.site.meta = Metadata(metadata)
  50. self.nodemeta = 'nodemeta.yaml'
  51. if hasattr(self.site.meta, 'nodemeta'):
  52. self.nodemeta = self.site.meta.nodemeta
  53. for node in self.site.content.walk():
  54. self.__read_node__(node)
  55. for resource in node.resources:
  56. if not hasattr(resource, 'meta'):
  57. resource.meta = Metadata({}, node.meta)
  58. if resource.source_file.is_text:
  59. self.__read_resource__(resource, resource.source_file.read_all())
  60. def __read_resource__(self, resource, text):
  61. """
  62. Reads the resource metadata and assigns it to
  63. the resource. Load meta data by looking for the marker.
  64. Once loaded, remove the meta area from the text.
  65. """
  66. self.logger.debug("Trying to load metadata from resource [%s]" % resource)
  67. yaml_finder = re.compile(
  68. r"^\s*(?:---|===)\s*\n((?:.|\n)+?)\n\s*(?:---|===)\s*\n*",
  69. re.MULTILINE)
  70. match = re.match(yaml_finder, text)
  71. if not match:
  72. self.logger.debug("No metadata found in resource [%s]" % resource)
  73. data = {}
  74. else:
  75. text = text[match.end():]
  76. data = match.group(1)
  77. if not hasattr(resource, 'meta') or not resource.meta:
  78. if not hasattr(resource.node, 'meta'):
  79. resource.node.meta = Metadata({})
  80. resource.meta = Metadata(data, resource.node.meta)
  81. else:
  82. resource.meta.update(data)
  83. self.__update_standard_attributes__(resource)
  84. self.logger.debug("Successfully loaded metadata from resource [%s]"
  85. % resource)
  86. return text or ' '
  87. def __update_standard_attributes__(self, obj):
  88. """
  89. Updates standard attributes on the resource and
  90. page based on the provided meta data.
  91. """
  92. if not hasattr(obj, 'meta'):
  93. return
  94. standard_attributes = ['is_processable', 'uses_template']
  95. for attr in standard_attributes:
  96. if hasattr(obj.meta, attr):
  97. setattr(obj, attr, getattr(obj.meta, attr))
  98. def __read_node__(self, node):
  99. """
  100. Look for nodemeta.yaml (or configured name). Load and assign it
  101. to the node.
  102. """
  103. nodemeta = node.get_resource(self.nodemeta)
  104. parent_meta = node.parent.meta if node.parent else self.site.meta
  105. if nodemeta:
  106. nodemeta.is_processable = False
  107. metadata = nodemeta.source_file.read_all()
  108. if hasattr(node, 'meta') and node.meta:
  109. node.meta.update(metadata)
  110. else:
  111. node.meta = Metadata(metadata, parent=parent_meta)
  112. else:
  113. node.meta = Metadata({}, parent=parent_meta)
  114. self.__update_standard_attributes__(node)
  115. def begin_node(self, node):
  116. """
  117. Read node meta data.
  118. """
  119. self.__read_node__(node)
  120. def begin_text_resource(self, resource, text):
  121. """
  122. Update the meta data again, just in case it
  123. has changed. Return text without meta data.
  124. """
  125. return self.__read_resource__(resource, text)