You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/10/14 21:00:11 UTC
[allura] 06/09: [#3938] remove stats/action logging
This is an automated email from the ASF dual-hosted git repository.
brondsem pushed a commit to branch db/8336
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 128121a8dea558bbd0d5f07924c3a3074b3a9a92
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri Oct 11 17:43:13 2019 -0400
[#3938] remove stats/action logging
---
Allura/allura/controllers/rest.py | 3 -
Allura/allura/ext/admin/admin_main.py | 29 ------
Allura/allura/lib/decorators.py | 90 -----------------
Allura/allura/lib/helpers.py | 70 -------------
Allura/allura/lib/utils.py | 54 +---------
Allura/allura/model/repo_refresh.py | 7 --
Allura/development.ini | 16 +--
Allura/docker-dev.ini | 16 +--
Allura/production-docker-example.ini | 16 +--
ForgeDiscussion/forgediscussion/model/forum.py | 1 -
ForgeTracker/forgetracker/model/ticket.py | 4 -
scripts/wiki-post.py | 135 -------------------------
12 files changed, 10 insertions(+), 431 deletions(-)
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index b3ae80d..04dac71 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -40,7 +40,6 @@ from allura.lib.decorators import require_post
from allura.lib.security import has_access
log = logging.getLogger(__name__)
-action_logger = h.log_action(log, 'API:')
class RestController(object):
@@ -386,8 +385,6 @@ class ProjectRestController(object):
c.app = app
if app.api_root is None:
raise exc.HTTPNotFound, name
- action_logger.info('', extra=dict(
- api_key=request.params.get('api_key')))
return app.api_root, remainder
@expose('json:')
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index d6c4f16..d63b734 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -331,14 +331,12 @@ class ProjectAdminController(BaseController):
if removal != c.project.removal:
M.AuditLog.log('change project removal status to %s', removal)
- h.log_action(log, 'change project removal status').info('')
c.project.removal = removal
c.project.removal_changed_date = datetime.utcnow()
if 'delete_icon' in kw:
M.ProjectFile.query.remove(dict(project_id=c.project._id, category=re.compile(r'^icon')))
c.project.set_tool_data('allura', icon_original_size=None)
M.AuditLog.log('remove project icon')
- h.log_action(log, 'remove project icon').info('')
g.post_event('project_updated')
redirect('overview')
elif 'delete' in kw:
@@ -346,76 +344,61 @@ class ProjectAdminController(BaseController):
config.get('allow_project_delete', True))
if allow_project_delete or not c.project.is_root:
M.AuditLog.log('delete project')
- h.log_action(log, 'delete project').info('')
plugin.ProjectRegistrationProvider.get().delete_project(
c.project, c.user)
redirect('overview')
elif 'undelete' in kw:
- h.log_action(log, 'undelete project').info('')
M.AuditLog.log('undelete project')
plugin.ProjectRegistrationProvider.get().undelete_project(
c.project, c.user)
redirect('overview')
if name and name != c.project.name:
- h.log_action(log, 'change project name').info('')
M.AuditLog.log('change project name to %s', name)
c.project.name = name
if short_description != c.project.short_description:
- h.log_action(log, 'change project short description').info('')
M.AuditLog.log('change short description to %s', short_description)
c.project.short_description = short_description
if summary != c.project.summary:
- h.log_action(log, 'change project summary').info('')
M.AuditLog.log('change summary to %s', summary)
c.project.summary = summary
category = category and ObjectId(category) or None
if category != c.project.category_id:
- h.log_action(log, 'change project category').info('')
M.AuditLog.log('change category to %s', category)
c.project.category_id = category
if external_homepage != c.project.external_homepage:
- h.log_action(log, 'change external home page').info('')
M.AuditLog.log('change external home page to %s',
external_homepage)
c.project.external_homepage = external_homepage
if video_url != c.project.video_url:
- h.log_action(log, 'change video url').info('')
M.AuditLog.log('change video url to %s', video_url)
c.project.video_url = video_url
if support_page != c.project.support_page:
- h.log_action(log, 'change project support page').info('')
M.AuditLog.log('change project support page to %s', support_page)
c.project.support_page = support_page
old_twitter = c.project.social_account('Twitter')
if not old_twitter or twitter_handle != old_twitter.accounturl:
- h.log_action(log, 'change project twitter handle').info('')
M.AuditLog.log('change project twitter handle to %s',
twitter_handle)
c.project.set_social_account('Twitter', twitter_handle)
old_facebook = c.project.social_account('Facebook')
if not old_facebook or facebook_page != old_facebook.accounturl:
if not facebook_page or 'facebook.com' in urlparse(facebook_page).netloc:
- h.log_action(log, 'change project facebook page').info('')
M.AuditLog.log(
'change project facebook page to %s', facebook_page)
c.project.set_social_account('Facebook', facebook_page)
if support_page_url != c.project.support_page_url:
- h.log_action(log, 'change project support page url').info('')
M.AuditLog.log('change project support page url to %s',
support_page_url)
c.project.support_page_url = support_page_url
if moved_to_url != c.project.moved_to_url:
- h.log_action(log, 'change project moved to url').info('')
M.AuditLog.log('change project moved to url to %s', moved_to_url)
c.project.moved_to_url = moved_to_url
if tracking_id != c.project.tracking_id:
- h.log_action(log, 'change project tracking ID').info('')
M.AuditLog.log('change project tracking ID to %s', tracking_id)
c.project.tracking_id = tracking_id
features = [f['feature'].strip() for f in features or []
if f.get('feature', '').strip()]
if features != c.project.features:
- h.log_action(log, 'change project features').info('')
M.AuditLog.log('change project features to %s', features)
c.project.features = features
@@ -588,9 +571,6 @@ class ProjectAdminController(BaseController):
if sp.get('delete'):
require_access(c.project, 'admin')
M.AuditLog.log('delete subproject %s', sp['shortname'])
- h.log_action(log, 'delete subproject').info(
- 'delete subproject %s', sp['shortname'],
- meta=dict(name=sp['shortname']))
p.removal = 'deleted'
plugin.ProjectRegistrationProvider.get().delete_project(
p, c.user)
@@ -602,9 +582,6 @@ class ProjectAdminController(BaseController):
if p.get('delete'):
require_access(c.project, 'admin')
M.AuditLog.log('uninstall tool %s', p['mount_point'])
- h.log_action(log, 'uninstall tool').info(
- 'uninstall tool %s', p['mount_point'],
- meta=dict(mount_point=p['mount_point']))
c.project.uninstall_app(p['mount_point'])
elif not new:
M.AuditLog.log('update tool %s', p['mount_point'])
@@ -617,9 +594,6 @@ class ProjectAdminController(BaseController):
require_access(c.project, 'create')
mount_point = new['mount_point'].lower() or h.nonce()
M.AuditLog.log('create subproject %s', mount_point)
- h.log_action(log, 'create subproject').info(
- 'create subproject %s', mount_point,
- meta=dict(mount_point=mount_point, name=new['mount_label']))
sp = c.project.new_subproject(mount_point)
sp.name = new['mount_label']
if 'ordinal' in new:
@@ -635,9 +609,6 @@ class ProjectAdminController(BaseController):
return
mount_point = new['mount_point'] or ep_name
M.AuditLog.log('install tool %s', mount_point)
- h.log_action(log, 'install tool').info(
- 'install tool %s', mount_point,
- meta=dict(tool_type=ep_name, mount_point=mount_point, mount_label=new['mount_label']))
App = g.entry_points['tool'][ep_name]
# pass only options which app expects
config_on_install = {
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index 8bb6413..f84c93c 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -136,96 +136,6 @@ def reconfirm_auth(func, *args, **kwargs):
return render({}, 'jinja', "allura:templates/reconfirm_auth.html")
-class log_action(object): # pragma no cover
-
- def __init__(self,
- logger=None,
- level=logging.INFO,
- msg=None,
- *args, **kwargs):
- if logger is None:
- logger = logging
- self._logger = logger
- self._level = level
- self._msg = msg
- self._args = args
- self._kwargs = kwargs
- self._extra_proto = dict(
- user=None,
- user_id=None,
- source=None,
- project_name=None,
- group_id=None)
-
- def __call__(self, func):
- self._func = func
- self._extra_proto.update(action=func.__name__)
- if self._msg is None:
- self._msg = func.__name__
- result = lambda *args, **kwargs: self._wrapper(*args, **kwargs)
- # assert not hasattr(func, 'decoration')
- if hasattr(func, 'decoration'):
- result.decoration = func.decoration
- return result
-
- def _wrapper(self, *args, **kwargs):
- result = None
- try:
- try:
- result = self._func(*args, **kwargs)
- except exc.HTTPServerError:
- raise
- except exc.HTTPException, e:
- result = e
- args = self._args
- kwargs = self._kwargs
- extra = kwargs.setdefault('extra', {})
- extra.update(self._make_extra(result))
- self._logger.log(self._level, self._msg,
- *self._args, **self._kwargs)
- return result
- except:
- args = self._args
- kwargs = self._kwargs
- extra = kwargs.setdefault('extra', {})
- extra.update(self._make_extra(result))
- kwargs['exc_info'] = sys.exc_info()
- self._logger.log(logging.ERROR, self._msg,
- *self._args, **self._kwargs)
- raise
-
- def _make_extra(self, result=None):
- '''Create a dict of extra items to be added to a log record
- '''
- extra = self._extra_proto.copy()
- # Save the client IP address
- extra.update(client_ip=utils.ip_address(request))
- # Save the user info
- user = getattr(request, 'user', None)
- if user:
- extra.update(user=user.username,
- user_id=user.id)
- # Save the project info
- if (result
- and isinstance(result, dict)
- and 'p' in result
- and result['p'] is not None):
- extra.update(
- source=result['p']['source'],
- project_name=result['p']['shortname'],
- group_id=result['p'].get('sf_id'))
- # Log the referer cookie if it exists
- referer_link = request.cookies.get('referer_link')
- if referer_link:
- referer_link = unquote(referer_link)
- try:
- referer_link = json.loads(referer_link)
- except ValueError:
- pass
- extra['referer_link'] = referer_link
- return extra
-
-
def getattr_(obj, name, default_thunk):
"Similar to .setdefault in dictionaries."
try:
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 28c08a1..82285f9 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -663,76 +663,6 @@ def twophase_transaction(*engines):
raise
-class log_action(object):
- extra_proto = dict(
- action=None,
- action_type=None,
- tool_type=None,
- tool_mount=None,
- project=None,
- neighborhood=None,
- username=None,
- url=None,
- ip_address=None)
-
- def __init__(self, logger, action):
- self._logger = logger
- self._action = action
-
- def log(self, level, message, *args, **kwargs):
- kwargs = dict(kwargs)
- extra = kwargs.setdefault('extra', {})
- meta = kwargs.pop('meta', {})
- kwpairs = extra.setdefault('kwpairs', {})
- for k, v in meta.iteritems():
- kwpairs['meta_%s' % k] = v
- extra.update(self._make_extra())
- self._logger.log(level, self._action + ': ' + message, *args, **kwargs)
-
- def info(self, message, *args, **kwargs):
- self.log(logging.INFO, message, *args, **kwargs)
-
- def debug(self, message, *args, **kwargs):
- self.log(logging.DEBUG, message, *args, **kwargs)
-
- def error(self, message, *args, **kwargs):
- self.log(logging.ERROR, message, *args, **kwargs)
-
- def critical(self, message, *args, **kwargs):
- self.log(logging.CRITICAL, message, *args, **kwargs)
-
- def exception(self, message, *args, **kwargs):
- self.log(logging.EXCEPTION, message, *args, **kwargs)
-
- def warning(self, message, *args, **kwargs):
- self.log(logging.EXCEPTION, message, *args, **kwargs)
- warn = warning
-
- def _make_extra(self):
- result = dict(self.extra_proto, action=self._action)
- try:
- if hasattr(c, 'app') and c.app:
- result['tool_type'] = c.app.config.tool_name
- result['tool_mount'] = c.app.config.options['mount_point']
- if hasattr(c, 'project') and c.project:
- result['project'] = c.project.shortname
- result['neighborhood'] = c.project.neighborhood.name
- if hasattr(c, 'user') and c.user:
- result['username'] = c.user.username
- else:
- result['username'] = '*system'
- try:
- result['url'] = request.url
- result['ip_address'] = utils.ip_address(request)
- except TypeError:
- pass
- return result
- except:
- self._logger.warning(
- 'Error logging to rtstats, some info may be missing', exc_info=True)
- return result
-
-
def paging_sanitizer(limit, page, total_count=sys.maxint, zero_based_pages=True):
"""Return limit, page - both converted to int and constrained to
valid ranges based on total_count.
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index fdf0340..8529a23 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -114,60 +114,8 @@ class lazy_logger(object):
raise AttributeError(name)
return getattr(self._logger, name)
-log = lazy_logger(__name__)
-
-class TimedRotatingHandler(logging.handlers.BaseRotatingHandler):
-
- def __init__(self, strftime_pattern):
- self.pattern = strftime_pattern
- self.last_filename = self.current_filename()
- logging.handlers.BaseRotatingHandler.__init__(
- self, self.last_filename, 'a')
-
- def current_filename(self):
- return os.path.abspath(datetime.datetime.utcnow().strftime(self.pattern))
- def shouldRollover(self, record):
- 'Inherited from BaseRotatingFileHandler'
- return self.current_filename() != self.last_filename
-
- def doRollover(self):
- self.stream.close()
- self.baseFilename = self.current_filename()
- if self.encoding:
- self.stream = codecs.open(self.baseFilename, 'w', self.encoding)
- else:
- self.stream = open(self.baseFilename, 'w')
-
-
-class StatsHandler(TimedRotatingHandler):
- fields = (
- 'action', 'action_type', 'tool_type', 'tool_mount', 'project', 'neighborhood',
- 'username', 'url', 'ip_address')
-
- def __init__(self,
- strftime_pattern,
- module='allura',
- page=1,
- **kwargs):
- self.page = page
- self.module = module
- TimedRotatingHandler.__init__(self, strftime_pattern)
-
- def emit(self, record):
- if not hasattr(record, 'action'):
- return
- kwpairs = dict(
- module=self.module,
- page=self.page)
- for name in self.fields:
- kwpairs[name] = getattr(record, name, None)
- kwpairs.update(getattr(record, 'kwpairs', {}))
- record.kwpairs = ','.join(
- '%s=%s' % (k, v) for k, v in sorted(kwpairs.iteritems())
- if v is not None)
- record.exc_info = None # Never put tracebacks in the rtstats log
- TimedRotatingHandler.emit(self, record)
+log = lazy_logger(__name__)
class CustomWatchedFileHandler(logging.handlers.WatchedFileHandler):
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 8585ffe..cf7615a 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -52,13 +52,6 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False, commits_
# the repo is empty, no need to continue
return
new_commit_ids = unknown_commit_ids(commit_ids)
- stats_log = h.log_action(log, 'commit')
- for ci in new_commit_ids:
- stats_log.info(
- '',
- meta=dict(
- module='scm-%s' % repo.repo_id,
- read='0'))
if not all_commits:
# Skip commits that are already in the DB
commit_ids = new_commit_ids
diff --git a/Allura/development.ini b/Allura/development.ini
index c20fece..ad56a7a 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -671,15 +671,15 @@ override_root = task ; TurboGears will use controllers/task.py as root controlle
keys = root, allura, sqlalchemy, paste, ew, taskdstatus, timermiddleware, tmw_details
[handlers]
-keys = console, stats, taskdstatus, timermiddleware
+keys = console, taskdstatus, timermiddleware
[formatters]
-keys = generic, stats, timermiddleware
+keys = generic, timermiddleware
; If you create additional loggers, add them as a key to [loggers]
[logger_root]
level = INFO
-handlers = console, stats
+handlers = console
[logger_allura]
level = INFO
@@ -729,12 +729,6 @@ args = (sys.stderr,)
level = NOTSET
formatter = generic
-[handler_stats]
-class = allura.lib.utils.StatsHandler
-args = ('rtstats.log', 'allura', 1)
-level = NOTSET
-formatter = stats
-
[handler_taskdstatus]
class = handlers.WatchedFileHandler
args = ('taskd_status.log', 'a')
@@ -754,10 +748,6 @@ formatter = timermiddleware
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
-[formatter_stats]
-format = %(asctime)s %(created)d %(kwpairs)s
-datefmt = %d/%b/%Y:%H:%M:%S UTC
-
[formatter_timermiddleware]
format = {"time": "%(asctime)s,%(msecs)03d", "level": "%(levelname)-5.5s", "name": "%(name)s", "message": %(message)s}
datefmt = %Y-%m-%d %H:%M:%S
diff --git a/Allura/docker-dev.ini b/Allura/docker-dev.ini
index fd9e3e8..980d8b8 100644
--- a/Allura/docker-dev.ini
+++ b/Allura/docker-dev.ini
@@ -72,15 +72,15 @@ override_root = task ; TurboGears will use controllers/task.py as root controlle
keys = root, allura, sqlalchemy, paste, ew, taskdstatus, timermiddleware, tmw_details
[handlers]
-keys = console, stats, taskdstatus, timermiddleware
+keys = console, taskdstatus, timermiddleware
[formatters]
-keys = generic, stats, timermiddleware
+keys = generic, timermiddleware
; If you create additional loggers, add them as a key to [loggers]
[logger_root]
level = INFO
-handlers = console, stats
+handlers = console
[logger_allura]
level = INFO
@@ -130,12 +130,6 @@ args = (sys.stderr,)
level = NOTSET
formatter = generic
-[handler_stats]
-class = allura.lib.utils.StatsHandler
-args = ('rtstats.log', 'allura', 1)
-level = NOTSET
-formatter = stats
-
[handler_taskdstatus]
class = handlers.WatchedFileHandler
args = ('taskd_status.log', 'a')
@@ -155,10 +149,6 @@ formatter = timermiddleware
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
-[formatter_stats]
-format = %(asctime)s %(created)d %(kwpairs)s
-datefmt = %d/%b/%Y:%H:%M:%S UTC
-
[formatter_timermiddleware]
format = {"time": "%(asctime)s,%(msecs)03d", "level": "%(levelname)-5.5s", "name": "%(name)s", "message": %(message)s}
datefmt = %Y-%m-%d %H:%M:%S
diff --git a/Allura/production-docker-example.ini b/Allura/production-docker-example.ini
index a1d2c4c..b5ba53c 100644
--- a/Allura/production-docker-example.ini
+++ b/Allura/production-docker-example.ini
@@ -97,15 +97,15 @@ override_root = task ; TurboGears will use controllers/task.py as root controlle
keys = root, allura, sqlalchemy, paste, ew, taskdstatus, timermiddleware, tmw_details
[handlers]
-keys = console, stats, taskdstatus, timermiddleware
+keys = console, taskdstatus, timermiddleware
[formatters]
-keys = generic, stats, timermiddleware
+keys = generic, timermiddleware
; If you create additional loggers, add them as a key to [loggers]
[logger_root]
level = INFO
-handlers = console, stats
+handlers = console
[logger_allura]
level = INFO
@@ -155,12 +155,6 @@ args = (sys.stderr,)
level = NOTSET
formatter = generic
-[handler_stats]
-class = allura.lib.utils.StatsHandler
-args = ('rtstats.log', 'allura', 1)
-level = NOTSET
-formatter = stats
-
[handler_taskdstatus]
class = handlers.WatchedFileHandler
args = ('taskd_status.log', 'a')
@@ -180,10 +174,6 @@ formatter = timermiddleware
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
-[formatter_stats]
-format = %(asctime)s %(created)d %(kwpairs)s
-datefmt = %d/%b/%Y:%H:%M:%S UTC
-
[formatter_timermiddleware]
format = {"time": "%(asctime)s,%(msecs)03d", "level": "%(levelname)-5.5s", "name": "%(name)s", "message": %(message)s}
datefmt = %Y-%m-%d %H:%M:%S
\ No newline at end of file
diff --git a/ForgeDiscussion/forgediscussion/model/forum.py b/ForgeDiscussion/forgediscussion/model/forum.py
index 5bc91f4..d65e7e2 100644
--- a/ForgeDiscussion/forgediscussion/model/forum.py
+++ b/ForgeDiscussion/forgediscussion/model/forum.py
@@ -193,7 +193,6 @@ class ForumThread(M.Thread):
if not self.first_post_id:
self.first_post_id = post._id
self.num_replies = 1
- h.log_action(log, 'posted').info('')
return post
def set_forum(self, new_forum):
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 780ba95..85210d3 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -683,7 +683,6 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
ticket.update_fields_basics(form_fields)
try:
session(ticket).flush(ticket)
- h.log_action(log, 'opened').info('')
return ticket
except OperationFailure, err:
if 'duplicate' in err.args[0]:
@@ -894,7 +893,6 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
('Summary', old.summary, self.summary),
('Status', old.status, self.status)]
if old.status != self.status and self.status in c.app.globals.set_of_closed_status_names:
- h.log_action(log, 'closed').info('')
g.statsUpdater.ticketEvent(
"closed", self, self.project, self.assigned_to)
for key in self.custom_fields:
@@ -1096,8 +1094,6 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
new_url = app_config.url() + str(self.ticket_num) + '/'
try:
session(self).flush(self)
- h.log_action(log, 'moved').info('Ticket %s moved to %s' %
- (prior_url, new_url))
break
except OperationFailure, err:
if 'duplicate' in err.args[0]:
diff --git a/scripts/wiki-post.py b/scripts/wiki-post.py
deleted file mode 100755
index 2a6fb40..0000000
--- a/scripts/wiki-post.py
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/python
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import types
-from sys import stdout
-import os
-import urllib
-from urllib2 import urlopen, HTTPError
-from urlparse import urlparse, urljoin
-from optparse import OptionParser
-from ConfigParser import ConfigParser
-
-
-def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
- """
- Returns a bytestring version of 's', encoded as specified in 'encoding'.
-
- If strings_only is True, don't convert (some) non-string-like objects.
-
- This function was borrowed from Django
- """
- if strings_only and isinstance(s, (types.NoneType, int)):
- return s
- elif not isinstance(s, basestring):
- try:
- return str(s)
- except UnicodeEncodeError:
- if isinstance(s, Exception):
- # An Exception subclass containing non-ASCII data that doesn't
- # know how to print itself properly. We shouldn't raise a
- # further exception.
- return ' '.join([smart_str(arg, encoding, strings_only,
- errors) for arg in s])
- return unicode(s).encode(encoding, errors)
- elif isinstance(s, unicode):
- r = s.encode(encoding, errors)
- return r
- elif s and encoding != 'utf-8':
- return s.decode('utf-8', errors).encode(encoding, errors)
- else:
- return s
-
-
-def generate_smart_str(params):
- for (key, value) in params:
- yield smart_str(key), smart_str(value)
-
-
-def urlencode(params):
- """
- A version of Python's urllib.urlencode() function that can operate on
- unicode strings. The parameters are first case to UTF-8 encoded strings and
- then encoded as per normal.
- """
- return urllib.urlencode([i for i in generate_smart_str(params)])
-
-
-class Signer(object):
-
- def __init__(self, token):
- self.token = token
-
- def __call__(self, path, params):
- if self.token is None:
- return params
- params.append(('token', self.token))
- return params
-
-
-def main():
- usage = 'usage: %prog [options] [PageName [file]]'
- op = OptionParser(usage=usage, description='Use a markdown file to create/update a wiki page')
- op.add_option('-c', '--config', metavar='CONFIG')
- op.add_option('-t', '--token', metavar='TOKEN')
- op.add_option('', '--anon', action='store_true')
- op.add_option('-u', '--url', metavar='URL')
- (options, args) = op.parse_args()
-
- pagename = None
- markdown = None
- method = 'GET'
-
- pagename_given = len(args) >= 1
- if pagename_given:
- pagename = args[0]
-
- filename_given = len(args) > 1
- if filename_given:
- method = 'PUT'
- f = open(args[1], 'r')
- markdown = f.read()
-
- config = ConfigParser()
- config.read(
- [str(os.path.expanduser('~/.forge-api.ini')), str(options.config)])
-
- token = None
- if not options.anon:
- token = options.token or config.get('keys', 'token')
-
- url = options.url or config.get('wiki', 'url')
- if pagename_given:
- url = urljoin(url, urllib.quote(pagename))
- print url
-
- sign = Signer(token)
- params = [('text', markdown)] if method == 'PUT' else []
- params = sign(urlparse(url).path, params)
- try:
- if method == 'PUT':
- result = urlopen(url, urlencode(params))
- else:
- result = urlopen(url + '?' + urlencode(params))
- stdout.write(result.read())
- except HTTPError, e:
- stdout.write(e.read())
-
-if __name__ == '__main__':
- main()