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.
 
 
 

164 lines
4.6 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Contains data structures and utilities for hyde.
  4. """
  5. from hyde.fs import File, Folder
  6. import codecs
  7. import yaml
  8. from hyde.util import getLoggerWithNullHandler
  9. logger = getLoggerWithNullHandler('hyde.engine')
  10. class Expando(object):
  11. """
  12. A generic expando class that creates attributes from
  13. the passed in dictionary.
  14. """
  15. def __init__(self, d):
  16. super(Expando, self).__init__()
  17. self.update(d)
  18. def __iter__(self):
  19. """
  20. Returns an iterator for all the items in the
  21. dictionary as key value pairs.
  22. """
  23. return self.__dict__.iteritems()
  24. def update(self, d):
  25. """
  26. Updates the expando with a new dictionary
  27. """
  28. d = d or {}
  29. if isinstance(d, dict):
  30. for key, value in d.items():
  31. self.set_expando(key, value)
  32. elif isinstance(d, Expando):
  33. self.update(d.__dict__)
  34. def set_expando(self, key, value):
  35. """
  36. Sets the expando attribute after
  37. transforming the value.
  38. """
  39. setattr(self, key, self.transform(value))
  40. def transform(self, primitive):
  41. """
  42. Creates an expando object, a sequence of expando objects or just
  43. returns the primitive based on the primitive's type.
  44. """
  45. if isinstance(primitive, dict):
  46. return Expando(primitive)
  47. elif isinstance(primitive, (tuple, list, set, frozenset)):
  48. seq = type(primitive)
  49. return seq(self.transform(attr) for attr in primitive)
  50. else:
  51. return primitive
  52. def to_dict(self):
  53. """
  54. Reverse transform an expando to dict
  55. """
  56. d = self.__dict__
  57. for k, v in d.iteritems():
  58. if isinstance(v, Expando):
  59. d[k] = v.to_dict()
  60. return d
  61. class Context(object):
  62. """
  63. Wraps the context related functions and utilities.
  64. """
  65. @staticmethod
  66. def load(sitepath, ctx):
  67. """
  68. Load context from config data and providers.
  69. """
  70. context = {}
  71. try:
  72. context.update(ctx.data.__dict__)
  73. for provider_name, resource_name in ctx.providers.__dict__.items():
  74. res = File(Folder(sitepath).child(resource_name))
  75. if res.exists:
  76. context[provider_name] = yaml.load(res.read_all())
  77. except AttributeError:
  78. # No context data found
  79. pass
  80. return context
  81. class Config(Expando):
  82. """
  83. Represents the hyde configuration file
  84. """
  85. def __init__(self, sitepath, config_file=None, config_dict=None):
  86. default_config = dict(
  87. content_root='content',
  88. deploy_root='deploy',
  89. media_root='media',
  90. layout_root='layout',
  91. media_url='/media',
  92. site_url='/',
  93. not_found='404.html',
  94. plugins = []
  95. )
  96. conf = dict(**default_config)
  97. self.sitepath = Folder(sitepath)
  98. conf.update(self.read_config(config_file))
  99. if config_dict:
  100. conf.update(config_dict)
  101. super(Config, self).__init__(conf)
  102. def read_config(self, config_file):
  103. """
  104. Reads the configuration file and updates this
  105. object while allowing for inherited configurations.
  106. """
  107. conf_file = self.sitepath.child(
  108. config_file if
  109. config_file else 'site.yaml')
  110. conf = {}
  111. if File(conf_file).exists:
  112. logger.info("Reading site configuration from [%s]", conf_file)
  113. with codecs.open(conf_file, 'r', 'utf-8') as stream:
  114. conf = yaml.load(stream)
  115. if 'extends' in conf:
  116. parent = self.read_config(conf['extends'])
  117. parent.update(conf)
  118. conf = parent
  119. return conf
  120. @property
  121. def deploy_root_path(self):
  122. """
  123. Derives the deploy root path from the site path
  124. """
  125. return self.sitepath.child_folder(self.deploy_root)
  126. @property
  127. def content_root_path(self):
  128. """
  129. Derives the content root path from the site path
  130. """
  131. return self.sitepath.child_folder(self.content_root)
  132. @property
  133. def media_root_path(self):
  134. """
  135. Derives the media root path from the site path
  136. """
  137. return self.sitepath.child_folder(self.media_root)
  138. @property
  139. def layout_root_path(self):
  140. """
  141. Derives the layout root path from the site path
  142. """
  143. return self.sitepath.child_folder(self.layout_root)