diff options
-rw-r--r-- | site/_static/logo.png | bin | 0 -> 6401 bytes | |||
-rw-r--r-- | site/_templates/rss.xml | 19 | ||||
-rw-r--r-- | site/blog.py | 140 | ||||
-rw-r--r-- | site/blog.rst | 16 | ||||
-rw-r--r-- | site/blog/first-post.rst | 7 | ||||
-rw-r--r-- | site/blog/second-post.rst | 7 | ||||
-rw-r--r-- | site/conf.py | 55 | ||||
-rw-r--r-- | site/contact.rst | 11 | ||||
-rw-r--r-- | site/contributing.rst | 19 | ||||
-rw-r--r-- | site/dev-requirements.txt | 3 | ||||
-rw-r--r-- | site/index.rst | 31 | ||||
-rw-r--r-- | site/installing.rst | 105 | ||||
-rw-r--r-- | site/tasks.py | 19 |
13 files changed, 432 insertions, 0 deletions
diff --git a/site/_static/logo.png b/site/_static/logo.png Binary files differnew file mode 100644 index 00000000..bc76697e --- /dev/null +++ b/site/_static/logo.png diff --git a/site/_templates/rss.xml b/site/_templates/rss.xml new file mode 100644 index 00000000..f6f9cbd1 --- /dev/null +++ b/site/_templates/rss.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <atom:link href="{{ atom }}" rel="self" type="application/rss+xml" /> + <title>{{ title }}</title> + <link>{{ link }}</link> + <description>{{ description }}</description> + <pubDate>{{ date }}</pubDate> + {% for link, title, desc, date in posts %} + <item> + <link>{{ link }}</link> + <guid>{{ link }}</guid> + <title><![CDATA[{{ title }}]]></title> + <description><![CDATA[{{ desc }}]]></description> + <pubDate>{{ date }}</pubDate> + </item> + {% endfor %} + </channel> +</rss> diff --git a/site/blog.py b/site/blog.py new file mode 100644 index 00000000..3b129ebf --- /dev/null +++ b/site/blog.py @@ -0,0 +1,140 @@ +from collections import namedtuple +from datetime import datetime +import time +import email.utils + +from sphinx.util.compat import Directive +from docutils import nodes + + +class BlogDateDirective(Directive): + """ + Used to parse/attach date info to blog post documents. + + No nodes generated, since none are needed. + """ + has_content = True + + def run(self): + # Tag parent document with parsed date value. + self.state.document.blog_date = datetime.strptime( + self.content[0], "%Y-%m-%d" + ) + # Don't actually insert any nodes, we're already done. + return [] + +class blog_post_list(nodes.General, nodes.Element): + pass + +class BlogPostListDirective(Directive): + """ + Simply spits out a 'blog_post_list' temporary node for replacement. + + Gets replaced at doctree-resolved time - only then will all blog post + documents be written out (& their date directives executed). + """ + def run(self): + return [blog_post_list('')] + + +Post = namedtuple('Post', 'name doc title date opener') + +def get_posts(app): + # Obtain blog posts + post_names = filter(lambda x: x.startswith('blog/'), app.env.found_docs) + posts = map(lambda x: (x, app.env.get_doctree(x)), post_names) + # Obtain common data used for list page & RSS + data = [] + for post, doc in sorted(posts, key=lambda x: x[1].blog_date, reverse=True): + # Welp. No "nice" way to get post title. Thanks Sphinx. + title = doc[0][0][0] + # Date. This may or may not end up reflecting the required + # *input* format, but doing it here gives us flexibility. + date = doc.blog_date + # 1st paragraph as opener. TODO: allow a role or something marking + # where to actually pull from? + opener = doc.traverse(nodes.paragraph)[0] + data.append(Post(post, doc, title, date, opener)) + return data + +def replace_blog_post_lists(app, doctree, fromdocname): + """ + Replace blog_post_list nodes with ordered list-o-links to posts. + """ + # Obtain blog posts + post_names = filter(lambda x: x.startswith('blog/'), app.env.found_docs) + posts = map(lambda x: (x, app.env.get_doctree(x)), post_names) + # Build "list" of links/etc + post_links = [] + for post, doc, title, date, opener in get_posts(app): + # Link itself + uri = app.builder.get_relative_uri(fromdocname, post) + link = nodes.reference('', '', refdocname=post, refuri=uri) + # Title, bolded. TODO: use 'topic' or something maybe? + link.append(nodes.strong('', title)) + date = date.strftime("%Y-%m-%d") + # Meh @ not having great docutils nodes which map to this. + html = '<div class="timestamp"><span>%s</span></div>' % date + timestamp = nodes.raw(text=html, format='html') + # NOTE: may group these within another element later if styling + # necessitates it + group = [timestamp, nodes.paragraph('', '', link), opener] + post_links.extend(group) + + # Replace temp node(s) w/ expanded list-o-links + for node in doctree.traverse(blog_post_list): + node.replace_self(post_links) + +def rss_timestamp(timestamp): + # Use horribly inappropriate module for its magical daylight-savings-aware + # timezone madness. Props to Tinkerer for the idea. + return email.utils.formatdate( + time.mktime(timestamp.timetuple()), + localtime=True + ) + +def generate_rss(app): + # Meh at having to run this subroutine like 3x per build. Not worth trying + # to be clever for now tho. + posts_ = get_posts(app) + # LOL URLs + root = app.config.rss_link + if not root.endswith('/'): + root += '/' + # Oh boy + posts = [ + ( + root + app.builder.get_target_uri(x.name), + x.title, + str(x.opener[0]), # Grab inner text element from paragraph + rss_timestamp(x.date), + ) + for x in posts_ + ] + location = 'blog/rss.xml' + context = { + 'title': app.config.project, + 'link': root, + 'atom': root + location, + 'description': app.config.rss_description, + # 'posts' is sorted by date already + 'date': rss_timestamp(posts_[0].date), + 'posts': posts, + } + yield (location, context, 'rss.xml') + +def setup(app): + # Link in RSS feed back to main website, e.g. 'http://paramiko.org' + app.add_config_value('rss_link', None, '') + # Ditto for RSS description field + app.add_config_value('rss_description', None, '') + # Interprets date metadata in blog post documents + app.add_directive('date', BlogDateDirective) + # Inserts blog post list node (in e.g. a listing page) for replacement + # below + app.add_node(blog_post_list) + app.add_directive('blog-posts', BlogPostListDirective) + # Performs abovementioned replacement + app.connect('doctree-resolved', replace_blog_post_lists) + # Generates RSS page from whole cloth at page generation step + app.connect('html-collect-pages', generate_rss) diff --git a/site/blog.rst b/site/blog.rst new file mode 100644 index 00000000..af9651e4 --- /dev/null +++ b/site/blog.rst @@ -0,0 +1,16 @@ +==== +Blog +==== + +.. blog-posts directive gets replaced with an ordered list of blog posts. + +.. blog-posts:: + + +.. The following toctree ensures blog posts get processed. + +.. toctree:: + :hidden: + :glob: + + blog/* diff --git a/site/blog/first-post.rst b/site/blog/first-post.rst new file mode 100644 index 00000000..7b075073 --- /dev/null +++ b/site/blog/first-post.rst @@ -0,0 +1,7 @@ +=========== +First post! +=========== + +A blog post. + +.. date:: 2013-12-04 diff --git a/site/blog/second-post.rst b/site/blog/second-post.rst new file mode 100644 index 00000000..c4463f33 --- /dev/null +++ b/site/blog/second-post.rst @@ -0,0 +1,7 @@ +=========== +Another one +=========== + +.. date:: 2013-12-05 + +Indeed! diff --git a/site/conf.py b/site/conf.py new file mode 100644 index 00000000..8c6f3aa4 --- /dev/null +++ b/site/conf.py @@ -0,0 +1,55 @@ +from datetime import datetime +import os +import sys + +import alabaster + + +# Add local blog extension +sys.path.append(os.path.abspath('.')) +extensions = ['blog'] +rss_link = 'http://paramiko.org' +rss_description = 'Paramiko project news' + +# Alabaster theme +html_theme_path = [alabaster.get_path()] +html_static_path = ['_static'] +html_theme = 'alabaster' +html_theme_options = { + 'logo': 'logo.png', + 'logo_name': 'true', + 'description': "A Python implementation of SSHv2.", + 'github_user': 'paramiko', + 'github_repo': 'paramiko', + 'gittip_user': 'bitprophet', + 'analytics_id': 'UA-18486793-2', + + 'link': '#3782BE', + 'link_hover': '#3782BE', + +} +html_sidebars = { + # Landing page (no ToC) + 'index': [ + 'about.html', + 'searchbox.html', + 'donate.html', + ], + # Inner pages get a ToC + '**': [ + 'about.html', + 'localtoc.html', + 'searchbox.html', + 'donate.html', + ] +} + +# Regular settings +project = u'Paramiko' +year = datetime.now().year +copyright = u'%d Jeff Forcier, 2003-2012 Robey Pointer' % year +master_doc = 'index' +templates_path = ['_templates'] +exclude_trees = ['_build'] +source_suffix = '.rst' +default_role = 'obj' diff --git a/site/contact.rst b/site/contact.rst new file mode 100644 index 00000000..b479f170 --- /dev/null +++ b/site/contact.rst @@ -0,0 +1,11 @@ +======= +Contact +======= + +You can get in touch with the developer & user community in any of the +following ways: + +* IRC: ``#paramiko`` on Freenode +* Mailing list: ``paramiko@librelist.com`` (see `the LibreList homepage + <http://librelist.com>`_ for usage details). +* This website's :doc:`blog </blog>`. diff --git a/site/contributing.rst b/site/contributing.rst new file mode 100644 index 00000000..b121e64b --- /dev/null +++ b/site/contributing.rst @@ -0,0 +1,19 @@ +============ +Contributing +============ + +How to get the code +=================== + +Our primary Git repository is on Github at `paramiko/paramiko +<https://github.com/paramiko/paramiko>`; please follow their instruction for +cloning to your local system. (If you intend to submit patches/pull requests, +we recommend forking first, then cloning your fork. Github has excellent +documentation for all this.) + + +How to submit bug reports or new code +===================================== + +Please see `this project-agnostic contribution guide +<http://contribution-guide.org>`_ - we follow it explicitly. diff --git a/site/dev-requirements.txt b/site/dev-requirements.txt new file mode 100644 index 00000000..524b8060 --- /dev/null +++ b/site/dev-requirements.txt @@ -0,0 +1,3 @@ +invoke>=0.6.1 +invocations>=0.4.4 +sphinx==1.1.3 diff --git a/site/index.rst b/site/index.rst new file mode 100644 index 00000000..7d203b62 --- /dev/null +++ b/site/index.rst @@ -0,0 +1,31 @@ +Welcome to Paramiko! +==================== + +Paramiko is a Python (2.5+) implementation of the SSHv2 protocol [#]_, +providing both client and server functionality. While it leverages a Python C +extension for low level cryptography (`PyCrypto <http://pycrypto.org>`_), +Paramiko itself is a pure Python interface around SSH networking concepts. + +This website covers project information for Paramiko such as contribution +guidelines, development roadmap, news/blog, and so forth. Detailed +usage and API documentation can be found at our code documentation site, +`docs.paramiko.org <http://docs.paramiko.org>`_. + +.. toctree:: + blog + installing + contributing + contact + + +.. rubric:: Footnotes + +.. [#] + SSH is defined in RFCs + `4251 <http://www.rfc-editor.org/rfc/rfc4251.txt>`_, + `4252 <http://www.rfc-editor.org/rfc/rfc4252.txt>`_, + `4253 <http://www.rfc-editor.org/rfc/rfc4253.txt>`_, and + `4254 <http://www.rfc-editor.org/rfc/rfc4254.txt>`_; + the primary working implementation of the protocol is the `OpenSSH project + <http://openssh.org>`_. Paramiko implements a large portion of the SSH + feature set, but there are occasional gaps. diff --git a/site/installing.rst b/site/installing.rst new file mode 100644 index 00000000..0d4dc1ac --- /dev/null +++ b/site/installing.rst @@ -0,0 +1,105 @@ +========== +Installing +========== + +Paramiko itself +=============== + +The recommended way to get Invoke is to **install the latest stable release** +via `pip <http://pip-installer.org>`_:: + + $ pip install paramiko + +.. note:: + Users who want the bleeding edge can install the development version via + ``pip install paramiko==dev``. + +We currently support **Python 2.5/2.6/2.7**, with support for Python 3 coming +soon. Users on Python 2.4 or older are urged to upgrade. Paramiko *may* work on +Python 2.4 still, but there is no longer any support guarantee. + +Paramiko has two dependencies: the pure-Python ECDSA module `ecdsa`, and the +PyCrypto C extension. `ecdsa` is easily installable from wherever you +obtained Paramiko's package; PyCrypto may require more work. Read on for +details. + +PyCrypto +======== + +`PyCrypto <https://www.dlitz.net/software/pycrypto/>`_ provides the low-level +(C-based) encryption algorithms we need to implement the SSH protocol. There +are a couple gotchas associated with installing PyCrypto: its compatibility +with Python's package tools, and the fact that it is a C-based extension. + +.. _pycrypto-and-pip: + +Possible gotcha on older Python and/or pip versions +--------------------------------------------------- + +We strongly recommend using ``pip`` to as it is newer and generally better than +``easy_install``. However, a combination of bugs in specific (now rather old) +versions of Python, ``pip`` and PyCrypto can prevent installation of PyCrypto. +Specifically: + +* Python = 2.5.x +* PyCrypto >= 2.1 (required for most modern versions of Paramiko) +* ``pip`` < 0.8.1 + +When all three criteria are met, you may encounter ``No such file or +directory`` IOErrors when trying to ``pip install paramiko`` or ``pip install +PyCrypto``. + +The fix is to make sure at least one of the above criteria is not met, by doing +the following (in order of preference): + +* Upgrade to ``pip`` 0.8.1 or above, e.g. by running ``pip install -U pip``. +* Upgrade to Python 2.6 or above. +* Downgrade to Paramiko 1.7.6 or 1.7.7, which do not require PyCrypto >= 2.1, + and install PyCrypto 2.0.1 (the oldest version on PyPI which works with + Paramiko 1.7.6/1.7.7) + + +C extension +----------- + +Unless you are installing from a precompiled source such as a Debian apt +repository or RedHat RPM, or using :ref:`pypm <pypm>`, you will also need the +ability to build Python C-based modules from source in order to install +PyCrypto. Users on **Unix-based platforms** such as Ubuntu or Mac OS X will +need the traditional C build toolchain installed (e.g. Developer Tools / XCode +Tools on the Mac, or the ``build-essential`` package on Ubuntu or Debian Linux +-- basically, anything with ``gcc``, ``make`` and so forth) as well as the +Python development libraries, often named ``python-dev`` or similar. + +For **Windows** users we recommend using :ref:`pypm`, installing a C +development environment such as `Cygwin <http://cygwin.com>`_ or obtaining a +precompiled Win32 PyCrypto package from `voidspace's Python modules page +<http://www.voidspace.org.uk/python/modules.shtml#pycrypto>`_. + +.. note:: + Some Windows users whose Python is 64-bit have found that the PyCrypto + dependency ``winrandom`` may not install properly, leading to ImportErrors. + In this scenario, you'll probably need to compile ``winrandom`` yourself + via e.g. MS Visual Studio. See `Fabric #194 + <https://github.com/fabric/fabric/issues/194>`_ for info. + + +.. _pypm: + +ActivePython and PyPM +===================== + +Windows users who already have ActiveState's `ActivePython +<http://www.activestate.com/activepython/downloads>`_ distribution installed +may find Paramiko is best installed with `its package manager, PyPM +<http://code.activestate.com/pypm/>`_. Below is example output from an +installation of Paramiko via ``pypm``:: + + C:\> pypm install paramiko + The following packages will be installed into "%APPDATA%\Python" (2.7): + paramiko-1.7.8 pycrypto-2.4 + Get: [pypm-free.activestate.com] paramiko 1.7.8 + Get: [pypm-free.activestate.com] pycrypto 2.4 + Installing paramiko-1.7.8 + Installing pycrypto-2.4 + C:\> diff --git a/site/tasks.py b/site/tasks.py new file mode 100644 index 00000000..e2d952b1 --- /dev/null +++ b/site/tasks.py @@ -0,0 +1,19 @@ +from invoke import Collection +from invocations import docs, testing + +# TODO: let from_module specify new name +api = Collection.from_module(docs) +# TODO: maybe allow rolling configuration into it too heh +api.configure({ + 'sphinx.source': 'api', + 'sphinx.target': 'api/_build', +}) +api.name = 'api' +site = Collection.from_module(docs) +site.name = 'site' +site.configure({ + 'sphinx.source': 'site', + 'sphinx.target': 'site/_build', +}) + +ns = Collection(testing.test, api=api, site=site) |