@@ -20,6 +20,7 @@ | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
# SOFTWARE. | |||
from collections import OrderedDict | |||
from getpass import getpass | |||
from hashlib import sha256 | |||
import logging | |||
@@ -44,6 +45,7 @@ except ImportError: | |||
from earwigbot.config.formatter import BotFormatter | |||
from earwigbot.config.node import ConfigNode | |||
from earwigbot.config.ordered_yaml import OrderedLoader | |||
from earwigbot.config.permissions import PermissionsDB | |||
from earwigbot.config.script import ConfigScript | |||
from earwigbot.exceptions import NoConfigError | |||
@@ -120,7 +122,7 @@ class BotConfig(object): | |||
filename = self._config_path | |||
with open(filename, 'r') as fp: | |||
try: | |||
self._data = yaml.load(fp) | |||
self._data = yaml.load(fp, OrderedLoader) | |||
except yaml.YAMLError: | |||
print "Error parsing config file {0}:".format(filename) | |||
raise | |||
@@ -270,12 +272,12 @@ class BotConfig(object): | |||
self._load() | |||
data = self._data | |||
self.components._load(data.get("components", {})) | |||
self.wiki._load(data.get("wiki", {})) | |||
self.irc._load(data.get("irc", {})) | |||
self.commands._load(data.get("commands", {})) | |||
self.tasks._load(data.get("tasks", {})) | |||
self.metadata._load(data.get("metadata", {})) | |||
self.components._load(data.get("components", OrderedDict())) | |||
self.wiki._load(data.get("wiki", OrderedDict())) | |||
self.irc._load(data.get("irc", OrderedDict())) | |||
self.commands._load(data.get("commands", OrderedDict())) | |||
self.tasks._load(data.get("tasks", OrderedDict())) | |||
self.metadata._load(data.get("metadata", OrderedDict())) | |||
self._setup_logging() | |||
if self.is_encrypted(): | |||
@@ -20,11 +20,13 @@ | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
# SOFTWARE. | |||
from collections import OrderedDict | |||
__all__ = ["ConfigNode"] | |||
class ConfigNode(object): | |||
def __init__(self): | |||
self._data = {} | |||
self._data = OrderedDict() | |||
def __repr__(self): | |||
return self._data | |||
@@ -99,4 +101,4 @@ class ConfigNode(object): | |||
return self._data.itervalues() | |||
def iteritems(self): | |||
return self.__dict__.iteritems() | |||
return self._data.iteritems() |
@@ -0,0 +1,107 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2009-2012 Ben Kurtovic <ben.kurtovic@verizon.net> | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||
# of this software and associated documentation files (the "Software"), to deal | |||
# in the Software without restriction, including without limitation the rights | |||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
# copies of the Software, and to permit persons to whom the Software is | |||
# furnished to do so, subject to the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included in | |||
# all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
# SOFTWARE. | |||
""" | |||
Based on: | |||
* https://gist.github.com/844388 | |||
* http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py | |||
with modifications. | |||
""" | |||
from collections import OrderedDict | |||
try: | |||
import yaml | |||
except ImportError: | |||
yaml = None | |||
__all__ = ["OrderedLoader", "OrderedDumper"] | |||
class OrderedLoader(yaml.Loader): | |||
"""A YAML loader that loads mappings into ordered dictionaries.""" | |||
def __init__(self, *args, **kwargs): | |||
super(OrderedLoader, self).__init__(*args, **kwargs) | |||
constructor = type(self).construct_yaml_map | |||
self.add_constructor(u"tag:yaml.org,2002:map", constructor) | |||
self.add_constructor(u"tag:yaml.org,2002:omap", constructor) | |||
def construct_yaml_map(self, node): | |||
data = OrderedDict() | |||
yield data | |||
value = self.construct_mapping(node) | |||
data.update(value) | |||
def construct_mapping(self, node, deep=False): | |||
if isinstance(node, yaml.MappingNode): | |||
self.flatten_mapping(node) | |||
else: | |||
raise yaml.constructor.ConstructorError(None, None, | |||
"expected a mapping node, but found {0}".format(node.id), | |||
node.start_mark) | |||
mapping = OrderedDict() | |||
for key_node, value_node in node.value: | |||
key = self.construct_object(key_node, deep=deep) | |||
try: | |||
hash(key) | |||
except TypeError, exc: | |||
raise yaml.constructor.ConstructorError( | |||
"while constructing a mapping", node.start_mark, | |||
"found unacceptable key ({0})".format(exc), | |||
key_node.start_mark) | |||
value = self.construct_object(value_node, deep=deep) | |||
mapping[key] = value | |||
return mapping | |||
class OrderedDumper(yaml.Dumper): | |||
"""A YAML dumper that dumps mappings into ordered dictionaries.""" | |||
def __init__(self, *args, **kwargs): | |||
super(OrderedDumper, self).__init__(*args, **kwargs) | |||
self.add_representer(OrderedDict, type(self).represent_dict) | |||
def represent_mapping(self, tag, mapping, flow_style=None): | |||
value = [] | |||
node = yaml.MappingNode(tag, value, flow_style=flow_style) | |||
if self.alias_key is not None: | |||
self.represented_objects[self.alias_key] = node | |||
best_style = True | |||
if hasattr(mapping, "items"): | |||
mapping = list(mapping.items()) | |||
for item_key, item_value in mapping: | |||
node_key = self.represent_data(item_key) | |||
node_value = self.represent_data(item_value) | |||
if not (isinstance(node_key, yaml.ScalarNode) and not | |||
node_key.style): | |||
best_style = False | |||
if not (isinstance(node_value, yaml.ScalarNode) and not | |||
node_value.style): | |||
best_style = False | |||
value.append((node_key, node_value)) | |||
if flow_style is None: | |||
if self.default_flow_style is not None: | |||
node.flow_style = self.default_flow_style | |||
else: | |||
node.flow_style = best_style | |||
return node |
@@ -38,6 +38,7 @@ except ImportError: | |||
yaml = None | |||
from earwigbot import exceptions | |||
from earwigbot.config.ordered_yaml import OrderedDumper | |||
__all__ = ["ConfigScript"] | |||
@@ -272,9 +273,10 @@ class ConfigScript(object): | |||
'unaffiliated/nickname'.""") | |||
host = self._ask("Your hostname on the IRC frontend:") | |||
if host: | |||
self.config._permissions.load() | |||
self.config._permissions.add_owner(host=host) | |||
self.config._permissions.add_admin(host=host) | |||
permdb = self.config._permissions | |||
permdb.load() | |||
permdb.add_owner(host=host) | |||
permdb.add_admin(host=host) | |||
else: | |||
frontend = {} | |||
@@ -360,8 +362,8 @@ class ConfigScript(object): | |||
self._pause() | |||
def _save(self): | |||
with open(self.config.path, "w") as stream: | |||
yaml.dump(self.data, stream=stream, default_flow_style=False) | |||
with open(self.config.path, "w") as strm: | |||
yaml.dump(self.data, strm, OrderedDumper, default_flow_style=False) | |||
def make_new(self): | |||
"""Make a new config file based on the user's input.""" | |||
@@ -20,6 +20,7 @@ | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
# SOFTWARE. | |||
from collections import OrderedDict | |||
from cookielib import LWPCookieJar, LoadError | |||
import errno | |||
from os import chmod, path | |||
@@ -192,7 +193,7 @@ class SitesDB(object): | |||
maxlag = config.wiki.get("maxlag") | |||
wait_between_queries = config.wiki.get("waitTime", 3) | |||
logger = self._logger.getChild(name) | |||
search_config = config.wiki.get("search", {}).copy() | |||
search_config = config.wiki.get("search", OrderedDict()).copy() | |||
if user_agent: | |||
user_agent = user_agent.replace("$1", __version__) | |||
@@ -204,7 +205,7 @@ class SitesDB(object): | |||
search_config["exclusions_db"] = self._exclusions_db | |||
if not sql: | |||
sql = config.wiki.get("sql", {}).copy() | |||
sql = config.wiki.get("sql", OrderedDict()).copy() | |||
for key, value in sql.iteritems(): | |||
if isinstance(value, basestring) and "$1" in value: | |||
sql[key] = value.replace("$1", name) | |||