bpython-0.12/0000755000175000017500000000000012061403162013406 5ustar simonsimon00000000000000bpython-0.12/doc/0000755000175000017500000000000012061403162014153 5ustar simonsimon00000000000000bpython-0.12/doc/sphinx/0000755000175000017500000000000012061403162015464 5ustar simonsimon00000000000000bpython-0.12/doc/sphinx/source/0000755000175000017500000000000012061403162016764 5ustar simonsimon00000000000000bpython-0.12/doc/sphinx/source/windows.rst0000644000175000017500000000106712031025543021214 0ustar simonsimon00000000000000.. _windows: Windows ======= When bpython was developed it only supported Linux and we grew support for other platforms as well. There are no official binaries for bpython on Windows (though this is something we plan on providing in the future). The easiest way to get `bpython.cli` (the curses frontend running) is to install an unofficial windows binary for pdcurses from: http://www.lfd.uci.edu/~gohlke/pythonlibs/#curses. After this you can just `pip install bpython` and run bpython like you would on a Linux system (e.g. by typing `bpython` on your prompt). bpython-0.12/doc/sphinx/source/sourcecode.rst0000644000175000017500000000274412031025543021660 0ustar simonsimon00000000000000.. _sourcecode: Sourcecode ========== Warning, large parts of source code are still undocumented till we include the automatic generation of this documentation by adding in restructed text comments. bpython.cli ----------- .. module:: cli :platform: POSIX :synopsis: Basic interpreter. .. function:: log(x) Function to log anything in x to /tmp/bpython.log .. function:: parsekeywordpairs(signature) Not documented yet. :param signature: string :rtype: dictionary .. function:: fixlongargs(f, argspec) Functions taking default arguments that are references to other objects whose str() is too big will cause breakage, so we swap out the object itself with the name it was referenced with in the source by parsing the source itself ! .. class:: FakeStdin .. method:: FakeStdin.__init__(self, interface) Take the curses Repl on init and assume it provides a get_key method which, fortunately, it does.""" .. method:: FakeStdin.isatty(self) Spoof into thinking this is a tty :rtype: Boolean :returns: True .. method:: FakeStdin.readline(self) I can't think of any reason why anything other than readline would be useful in the context of an interactive interpreter so this is the only one I've done anything with. The others are just there in case someone does something weird to stop it from blowing up.""" :rtype: string bpython.keys ------------ .. module:: keys :platform: POSIX :synopsis: Keyboard mappings bpython-0.12/doc/sphinx/source/themes.rst0000644000175000017500000000305512031025543021006 0ustar simonsimon00000000000000.. _themes: Themes ====== This chapter is about bpython's themeing capabalities. bpython uses .theme files placed in your ~/.bpython directory. You can set the theme in the :ref:`configuration_color_scheme` option in your `~/bpython/config`` file (:ref:`configuration`). You can find some of our themes in our gallery on our website: http://bpython-interpreter.org/themes/ Available Colors ---------------- * k = black * r = red * g = green * y = yellow * b = blue * m = magenta * c = cyan * w = white * d = default, this will make the switch default to the bpython default theme Any letter writting uppercase will make the switch bold. Available Switches ------------------ * keyword * name * comment * string * error * number * operator * punctuation * token * background * output * main * prompt * prompt_more Default Theme ------------- The default theme included in bpython is as follows: .. code-block:: python :linenos: # Each letter represents a colour marker: # k, r, g, y, b, m, c, w, d # which stands for: # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default # Capital letters represent bold # Copy to ~/.bpython/foo.theme and set "color_scheme = foo" in ~/bpython/config [syntax] keyword = y name = c comment = b string = m error = r number = G operator = Y punctuation = y token = C paren = R [interface] # XXX: gnome-terminal appears to be braindead. The cursor will disappear unless # you set the background colour to "d". background = k output = w main = c prompt = c prompt_more = g bpython-0.12/doc/sphinx/source/man-bpython-config.rst0000644000175000017500000000126512061377417023236 0ustar simonsimon00000000000000:orphan: bpython-config manual page ========================== Synopsis -------- **$XDG_CONFIG_HOME/bpython/config** Description ----------- The configuration file contains various options controlling the behaviour of :program:`bpython`. .. include:: configuration-options.rst :end-before: .. _configuration_color_scheme: .. include:: configuration-options.rst :start-after: .. _configuration_color_scheme: Author ------ :program:`bpython` was written by Robert Anthony Farrell and his bunch of loyal followers. This manual page was written by Jørgen Pedersen Tjernø , for the Debian project (but may be used by others). bpython-0.12/doc/sphinx/source/index.rst0000644000175000017500000000125712031025543020632 0ustar simonsimon00000000000000.. bpython documentation master file, created by sphinx-quickstart on Mon Jun 8 11:58:16 2009. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. bpython documentation ===================== Welcome to the bpython documentation files. This is where you can find all about using and customizing the bpython interpreter. If you want to find out more about the bpython interpreter you can visit our website: http://bpython-interpreter.org Contents: .. toctree:: :maxdepth: 2 authors configuration themes releases community django windows changelog sourcecode bpaste tips bpdb bpython-0.12/doc/sphinx/source/bpdb.rst0000644000175000017500000000061612031025543020430 0ustar simonsimon00000000000000.. _bpdb: bpdb ==== To enable bpython support within pdb, start pdb with the following code: .. code-block:: python import bpdb bpdb.set_trace() This will drop you into bpdb instead of pdb, which works exactly like pdb except that you can additionally start bpython at the current stack frame by issuing the command `Bpython` or `B`. You can exit bpython with `^D` to return to bpdb. bpython-0.12/doc/sphinx/source/changelog.rst0000644000175000017500000004125512031025543021454 0ustar simonsimon00000000000000Changelog ========= Since v0.11 ___________ * Added a new pastebin_helper config option to name an executable that should perform pastebin upload on bpython's behalf. If set, this overrides pastebin_url. Data is supplied to the helper via STDIN, and it is expected to return a pastebin URL as the first word of its output. * Fixed a bug causing pastebin upload to fail after a previous attempt was unsuccessful. A duplicate pastebin error would be displayed in this case, despite the original upload having failed. v0.11 ----- A bugfix/cleanup release .The fixed bugs are: * #204: "import math" not autocompleting on python 3.2 Otherwise lots of small additions to the to be replacement for our ncurses frontend, the urwid frontend. I'd like to specifically thank Amjith Ramanujam for his work on history search which was further implemented and is in working order right now. v0.10.1 ------- A bugfix release. The fixed bugs are: * #197: find_modules crashes on non-readable directories * #198: Source tarball lacks .po files v0.10 ----- As a highlight of the release, Michele Orrù added i18n support to bpython. Some issues have been resolved as well: * Config files are now located according to the XDG Base Directory Specification. The support for the old bpythonrc files has been dropped and ~/.bpython.ini as config file location is no longer supported. See issue #91. * Fixed some issues with tuple unpacking in argspec. See issues #133 and #138. * Fixed a crash with non-ascii filenames in import completion. See issue #139. * Fixed a crash caused by inspect.findsource() raising an IndexError which happens in some situations. See issue #94. * Non-ascii input should work now under Python 3. * Issue #165: C-a and C-e do the right thing now in urwid. * The short command-line option "-c config" was dropped as it conflicts with vanilla Python's "-c command" option. See issue #186. v0.9.7.1 -------- A bugfix release. The fixed bugs are: * #128: bpython-gtk is broken * #134: crash when using pastebin and no active internet connection v0.9.7 ------ Well guys. It's been some time since the latest release, six months have passed We have added a whole slew of new features, and closed a number of bugs as well. We also have a new frontend for bpython. Marien Zwart contributed a urwid frontend as an alternative for the curses frontend. Be aware that there still is a lot to fix for this urwid frontend (a lot of the keyboard shortcuts do not yet work for example) but please give it a good spin. Urwid also optionally integrates with a Twisted reactor and through that with things like the GTK event loop. At the same time we have done a lot of work on the GTK frontend. The GTK frontend is now 'usable'. Please give that a spin as well by running python-gtk on you system. We also welcome a new contributor in the name of Michele Orrù who we hope will help us fix even more bugs and improve functionality. As always, please submit any bugs you might find to our bugtracker. * Pastebin confirmation added; we were getting a lot of people accidentally pastebinning sensitive information so I think this is a good idea. * Don't read PYTHONSTARTUP when executed with -i. * BPDB was merged in. BPDB is an extension to PDB which allows you to press B in a PDB session which will let you be dropped into a bpython sessions with the current PDB locals(). For usage, see the documentation. * The clear word shortcut (default: C-w) now deletes to the buffer. * More tests have been added to bpython. * The pastebin now checks for a previous paste (during the session) with the exact same content to guard against twitchy fingers pastebinning multiple times. * Let import completion return "import " instead of "import". * GTK now has pastebin, both for full log as well as the current selection. * GTK now has write2file. * GTK now has a menu. * GTK now has a statusbar. * GTK now has show source functionality. * GTK saves the pastebin url to the clipboard. * GTK now has it's own configuration section. * Set focus to the GTK text widget to allow for easier embedding in PIDA and others which fixes issues #121. * #87: Add a closed attribute to Repl to fix mercurial.ui.ui expecting stderr to have this attribute. * #108: Unicode characters in docsrting crash bpython * #118: Load_theme is not defined. * #99: Configurable font now documented. * #123: Pastebin can't handle 'ESC' key * #124: Unwanted input when using / keys in the statusbar prompt v0.9.6.2 -------- Unfortunately another bugfix release as I (Bob) broke py3 support. * #84: bpython doesn't work with Python 3 Thanks very much to Henry Prêcheur for both the bug report and the patch. v0.9.6.1 -------- A quick bugfix release (this should not become a habit). * #82: Crash on saving file. v0.9.6 ------ A bugfix/feature release (and a start at gtk). Happy Christmas everyone! * #67: Make pastebin URL really configurable. * #68: Set a__main__ module and set interpreter's namespace to that module. * #70: Implement backward completion on backward tab. * #62: Hide matches starting with a _ unless explicitly typed. * #72: Auto dedentation * #78: Theme without a certain value raises exception - add the possibility for a banner to be shown on bpython startup (when embedded or in code) written by Caio Ramao. - add a hack to add a write() method to our fake stdin object - Don't use curses interface when stdout is not attached to a terminal. - PEP-8 conformance. - Only restore indentation when inside a block. - Do not decrease the lineno in tracebacks for Py3 - Do not add internal code to history. - Make paren highlighting more accurate. - Catch SyntaxError in import completion. - Remove globals for configuration. - rl_history now stays the same, also after undo. v0.9.5.2 -------- A bugfix release. Fixed issues: * #60: Filename expansion: Cycling completions and deleting * #61: Filename expansion: Directory names with '.'s get mangled Other fixes without opened issues: * Encode items in the suggestion list properly * Expand usernames in file completion correctly * future imports in startup scripts can influence interpreter's behaviour now * Show the correct docstring for types without a own __init__ method v0.9.5.1 -------- Added missing data files to the tarball. v0.9.5 ------ Fixed issues: * #25 Problems with DEL, Backspace and C-u over multiple lines * #49 Sending last output to $PAGER * #51 Ability to embed bpython shell into an existing script * #52 FakeStdin.readlines() is broken * #53 Error on printing null character * #54 Parsing/introspection ncurses viewer neglects parenthesis bpython has added a view source shortcut to show the source of the current function. The history file is now really configurable. This issue was reported in Debian's bugtracker. bpython has now some basic support for Python 3 (requires Pygments >=1.1.1). As a result, setuptools is now optional. The pastebin URL is now configurable and the default pastebin is now bpaste.net Argument names are now shown as completion suggestions and one can tab through the completion list. v0.9.4 ------ Bugfix release (mostly) * when typing a float literal bpython autocompletes int methods (#36) * Autocompletion for file names (#40) * Indenting doesn't reset (#27) * bpython configuration has moved from ~/.bpython.ini to ~/.bpython/config (currently still supporting fallback) * leftovers of statusbar when exiting bpython cleaned up * bpython now does not crash when a 'popup' goes out of window bounds * numerous fixes and improvements to parentheses highlighting * made *all* keys configurable (except for arrow keys/pgup/pgdown) v0.9.3 ------ This release was a true whopper! * Full unicode support * Configurable hotkey support * Theming support * Pastemode, disables syntax highlighting during a paste for faster pasting, highlights when done * Parentheses matching * Argument highlighting v0.9.2 ------ * help() now uses an external pager if available. * Fix for highlighting prefixed strings. * Fix to reset string highlighting after a SyntaxError. * bpython now uses optparse for option parsing and it supports --version now. * Configuration files are no longer passed by the first command line argument but by the -c command line switch. * Fix for problem related to editing lines in the history: http://bitbucket.org/bobf/bpython/issue/10/odd-behaviour-when-editing-commands-in-the-history v0.9.1 ------ * Fixed a small but annoying bug with sys.argv ini file passing * Fix for Python 2.6 to monkeypatch they way it detects callables in rlcompleter * Config file conversion fix v0.9.0 ------ * Module import completion added. * Changed to paste.pocoo.org due to rafb.net no longer offering a pastebin service. * Switched to .ini file format for config file. * White background-friendly colour scheme added. * C-l now clears the screen. * SyntaxError now correctly added to history to prevent it garbling up on a redraw. Probably some other things, but I hate changelogs. :) v0.8.0 ------ It's been a long while since the last release and there've been numerous little bugfixes and extras here and there so I'm putting this out as 0.8.0. Check the hg commit history if you want more info: http://bitbucket.org/bobf/bpython/ v0.7.2 ------ Menno sent me some patches to fix some stuff: * Socket error handled when submitting to a pastebin. * Resizing could crash if you resize small enough. Other stuff: * 'self' in arg list is now highlighted a different colour. * flush_output option added to config to control whether output is flushed to stdout or not on exit. * Piping something to bpython made it lock up as stdin was not the keyboard - bpython just executes stdin and exits instead of trying to do something clever. * Mark Florisson (eggy) gave me a patch that stops weird breakage when unicode objects get added into the output buffer - they now get encoded into the output encoding. * Bohdan Vlasyuk sent me a patch that fixes a problem with the above patch from Mark if sys.__stdout__.encoding didn't exist. * Save to file now outputs executable code (i.e. without the >>> and ... and with "# OUT: " prepended to all output lines). I never used this feature much but someone asked for this behaviour. v0.7.1 ------ * Added support for a history file, defaults to ~/.pythonhist and 100 lines but is configurable from the rc file (see sample-rc). * Charles Duffy has added a yank/put thing - C-k and C-y. He also ran the code through some PEP-8 checker thing and fixed up a few old habits I manage to break but didn't manage to fix the code to reflect this - thank you! * Jørgen Tjernø has fixed up the autoindentation issues we encountered when bringing soft tabs in. * SyntaxError, ValueError and OverflowError are now caught properly (code.InteractiveInterpreter treats these as different to other exceptions as it doesn't print the whole traceback, so a different handler is called). This was discovered as I was trying to stop autoindentation from occurring on a SyntaxError, which has also been fixed. * '.' now in sys.path on startup. v0.7.0 ------ C-d behaviour changed so it no longer exits if the current line isn't empty. Extra linebreak added to end of stdout flush. pygments and pyparsing are now dependencies. Jørgen Tjernø has done lots of cool things like write a manpage and .desktop file and improved the way tabbing works and also added home, end and del key handling as well as C-w for deleting words - thanks a lot! raw_input() and all its friends now work fine. PYTHONSTARTUP handled without blowing up on stupid errors (it now parses the file at once instead of feeding it to the repl line-by-line). v0.6.4 ------ KeyboardInterrupt handler clears the list window properly now. v0.6.3 ------ Forgot to switch rpartition to split for 2.4 compat. v0.6.2 ------ The help() now works (as far as I can see) exactly the same as the vanilla help() in the regular interpreter. I copied some code from pydoc.py to make it handle the special cases, e.g. help('keywords') help('modules') etc. v0.6.1 ------ Somehow it escaped my attention that the list window was never fully using the rightmost column, except for the first row. This is because me and numbers don't have the best relationship. I think stability is really improving with the latest spat of bugfixes, keep me informed of any bugs. v0.6.0 ------ No noticeable changes except that bpython should now work with Python 2.4. Personally I think it's silly to make a development tool work with an out of date version of Python but some people seem to disagree. The only real downside is that I had to do a horrible version of all() using reduce(), otherwise there's no real differences in the code. v0.5.3 ------ Now you can configure a ~/.bpythonrc file (or pass a rc file at the command line (bpython /foo/bar). See README for details. v0.5.2 ------ help() actually displays the full help page, and I fixed up the ghetto pager a little. v0.5.1 ------ Now you can hit tab to display the autocomplete list, rather than have it pop up automatically as you type which, apparently, annoys Brendogg. v0.5.0 ------ A few people have commented that the help() built-in function doesn't work so well with bpython, since Python will try to output the help string to PAGER (usually "less") which obviously makes everything go wrong when curses is involved. With a bit of hackery I've written my own ghetto pager and injected my own help function into the interpreter when it initialises in an attempt to rectify this. As such, it's pretty untested but it seems to be working okay for me. Suggestions/bug reports/patches are welcome regarding this. v0.4.2 ------ Well, hopefully we're one step closer to making the list sizing stuff work. I really hate doing code for that kind of thing as I never get it quite right, but with perseverence it should end up being completely stable; it's not the hardest thing in the world. Various cosmetic fixes have been put in at the request of a bunch of people who were kind enough to send me emails regarding their experiences. PYTHONSTARTUP is now dealt with and used properly, as per the vanilla interpreter. v0.4.1 ------ It looks like the last release was actually pretty bug-free, aside from one tiny bug that NEVER ACTUALLY HAPPENS but someone was bugging me about it anyway, oh well. v0.4.0 ------ It's been quite a long time since the last update, due to several uninteresting and invalid excuses, but I finally reworked the list drawing procedures so the crashing seems to have been taken care of to an extent. If it still crashes, the way I've written it will hopefully allow a much more robust way of fixing it, one that might actually work. v0.3.2 ------ Thanks to Aaron Gallagher for pointing out a case where the hugely inefficient list generation routines were actually making a significant issue; they're much more efficient now and should hopefully not cause any more problems. v0.3.1 ------ Thanks to Klaus Alexander Seis for the expanduser() patch. Auto indent works on multiple levels now. v0.3.0 ------ Now with auto-indent. Let me know if it's annoying. v0.2.4 ------ Thanks a lot to Angus Gibson for submitting a patch to fix a problem I was having with initialising the keyboard stuff in curses properly. Also a big thanks to John Beisley for providing the patch that shows a class __init__ method's argspec on class instantiation. I've fixed up the argspec display so it handles really long argspecs (e.g. subprocess.Popen()) and doesn't crash if something horrible happens (rather, it avoids letting something horrible happen). I decided to add a key that will get rid of the autocomplete window, since it can get in the way. C-l seemed like a good choice, since it would work well as a side-effect of redrawing the screen (at least that makes sense to me). In so doing I also cleaned up a lot of the reevaluating and resizing code so that a lot of the strange output seen on Rewind/resize seems to be gone. v0.2.3 ------ The fix for the last bug broke the positioning of the autocomplete box, whoops. v0.2.2 ------ That pesky bug keeps coming up. I think it's finally nailed but it's just a matter of testing and hoping. I hate numbers. v0.2.1 ------ I'm having a bit of trouble with some integer division that's causing trouble when a certain set of circumstances arise, and I think I've taken care of that little bug, since it's a real pain in the ass and only creeps up when I'm actually doing something useful, so I'll test it for a bit and release it as hopefully a bug fixed version. v0.2.0 ------ A little late in the day to start a changelog, but here goes... This version fixed another annoying little bug that was causing crashes given certain exact circumstances. I always find it's the way with curses and sizing of windows and things... I've also got bpython to try looking into pydoc if no matches are found for the argspec, which means the builtins have argspecs too now, hooray. bpython-0.12/doc/sphinx/source/configuration-options.rst0000644000175000017500000001333312061377417024076 0ustar simonsimon00000000000000General ------- This refers to the ``[general]`` section in your `$XDG_CONFIG_HOME/bpython/config` file. auto_display_list ^^^^^^^^^^^^^^^^^ Display the autocomplete list as you type (default: True). When this is off, you can hit tab to see the suggestions. autocomplete_mode ^^^^^^^^^^^^^^^^^ There are three modes for autocomplete. simple, substring, and fuzzy. Simple matches methods with a common prefix, substring matches methods with a common subsequence, and fuzzy matches methods with common characters (default: simple). .. versionadded:: 0.12 syntax ^^^^^^ Syntax highlighting as you type (default: True). arg_spec ^^^^^^^^ Display the arg spec (list of arguments) for callables, when possible (default: True). hist_file ^^^^^^^^^ History file (default: ``~/.pythonhist``). paste_time ^^^^^^^^^^ The time between lines before pastemode is activated in seconds (default: 0.02). hist_length ^^^^^^^^^^^ Number of lines to store in history (set to 0 to disable) (default: 100) tab_length ^^^^^^^^^^ Soft tab size (default 4, see pep-8) pastebin_url ^^^^^^^^^^^^ The pastebin url to post to (without a trailing slash). This pastebin has to be a pastebin which uses LodgeIt. Examples are: http://paste.pocoo.org/xmlrpc/ and http://bpaste.net/xmlrpc/ (default: http://bpaste.net/xmlrpc/) pastebin_private ^^^^^^^^^^^^^^^^ If the pastebin supports a private option to make a random paste id, use it. Default: True). .. versionadded:: 0.12 pastebin_show_url ^^^^^^^^^^^^^^^^^ The url under which the new paste can be reached. ``$paste_id`` will be replaced by the ID of the new paste. Examples are: http://bpaste.net/show/$paste_id/ and http://paste.pocoo.org/show/$paste_id/ (default: http://bpaste.net/show/$paste_id/) pastebin_helper ^^^^^^^^^^^^^^^ The name of a helper executable that should perform pastebin upload on bpython's behalf. If set, this overrides `pastebin_url`. It also overrides `pastebin_show_url`, as the helper is expected to return the full URL to the pastebin as the first word of its output. The data is supplied to the helper via STDIN. An example helper program is ``pastebinit``, available for most systems. The following helper program can be used to create `gists `_: .. code-block:: python #!/usr/bin/env python import sys import urllib2 import json def do_gist_json(s): """ Use json to post to github. """ gist_public = False gist_url = 'https://api.github.com/gists' data = {'description': None, 'public': None, 'files' : { 'sample': { 'content': None } }} data['description'] = 'Gist from BPython' data['public'] = gist_public data['files']['sample']['content'] = s req = urllib2.Request(gist_url, json.dumps(data), {'Content-Type': 'application/json'}) try: res = urllib2.urlopen(req) except HTTPError, e: return e try: json_res = json.loads(res.read()) return json_res['html_url'] except HTTPError, e: return e if __name__ == "__main__": s = sys.stdin.read() print do_gist_json(s) .. versionadded:: 0.12 .. _configuration_color_scheme: color_scheme ^^^^^^^^^^^^ See :ref:`themes` for more information. Color schemes should be put in ``$XDG_CONFIG_HOME/bpython/`` e.g to use the theme ``$XDG_CONFIG_HOME/bpython/foo.theme`` set ``color_scheme = foo`` If you set the colorscheme to `foo` this will be translated to ``$XDG_CONFIG_HOME/bpython/foo.theme`` so be sure to put the file in that directory. Leave blank or set to "default" to use the default (builtin) theme. flush_output ^^^^^^^^^^^^ Whether to flush all output to stdout on exit (default: True). Keyboard -------- This section refers to the ``[keyboard]`` section in your ``$XDG_CONFIG_HOME/bpython/config``. You can set various keyboard shortcuts to be used by bpython. However, we have yet to map all keys to their respective control codes. If you configure a key combination which is not yet supported by bpython it will raise an exception telling you the key does not exist in bpython.keys. Valid keys are: * Control + any alphanumeric character (C-a through A-z, also a few others). * Any function key ranging from F1 to F12. pastebin ^^^^^^^^ Default: last_output ^^^^^^^^^^^ Default: F9 Shows the last output in the systems $PAGER. save ^^^^ Default: C-s Saves the current session to a file (prompts for filename) undo ^^^^ Default: C-r Rewinds the last action. up_one_line ^^^^^^^^^^^ Default: C-p Move the cursor up, by one line. down_one_line ^^^^^^^^^^^^^ Default: C-n Move the cursor down, by one line. cut_to_buffer ^^^^^^^^^^^^^ Default: C-k Cuts the current line to the buffer. search ^^^^^^ Default: C-o Search up for any lines containing what is on the current line. yank_from_buffer ^^^^^^^^^^^^^^^^ Default: C-y Pastes the current line from the buffer (the one you previously cutted) clear_word ^^^^^^^^^^ Default: C-w Clear the word the cursor is currently on. clear_line ^^^^^^^^^^ Default: C-u Clears to the beginning of the line. clear_screen ^^^^^^^^^^^^ Default: C-l Clears the screen to the top. show_source ^^^^^^^^^^^ Default: F2 Shows the source of the currently being completed (python) function. exit ^^^^ Default: C-d Exits bpython (use on empty line) CLI --- This refers to the ``[cli]`` section in your config file. suggestion_width ^^^^^^^^^^^^^^^^ Default: 0.8 The width of the suggestion window in percent of the terminal width. .. versionadded:: 0.9.8 trim_prompts ^^^^^^^^^^^^ Default: False Trims lines starting with '>>> ' when set to True. GTK --- This refers to the ``[gtk]`` section in your `$XDG_CONFIG_HOME/bpython/config` file. font ^^^^ Default: Monospace 10 The font to be used by the GTK version. bpython-0.12/doc/sphinx/source/releases.rst0000644000175000017500000000207012031025543021320 0ustar simonsimon00000000000000.. _releases: Releases ======== Release schedule ---------------- bpython does not have a set release cycle. The developers will decide together when the time is ripe to release a version. For information what happens after the decision is made to make a release you should read the 'Release Path' section. Release Path ------------ After it is decided to release a new version of bpython the following checklist is followed: * The repository is frozen, nobody pushes until the version is built. * Bob (:ref:`authors`) makes a tarball of the new version and sends it to Simon (:ref:`authors`) who will host it on the bpython website. * The package is then downloaded by all of the people who like to test it. * Everybody checks if there are no great problems: * Version numbers correct? * CHANGELOG is correct? * AUTHORS? * After everybody says 'yes' the website and pypi are updated to point to this new version. * Simon (:ref:`authors`) also checks if all numbers on the website have been updated. * 24 hours later package maintainers could update their stuff. bpython-0.12/doc/sphinx/source/authors.rst0000644000175000017500000000212412031025543021202 0ustar simonsimon00000000000000.. _authors: Authors ======= If you contributed to bpython and want to be on this list please find us (:ref:`community`) and let us know! bpython is written and maintained by Bob Farrell and Andreas Stuehrk , . Other contributors are (in alphabetical order): * Federico Ceratto * Ingrid Cheung * Martha Girdler * Eike Hein * Allison Kaptur * Brandon Navra * Michele Orrù * Pavel Panchekha * Sebastian Ramacher * Amjith Ramanujam * Simon de Vlieger * Marien Zwart A big thanks goes out to all the people who help us out by either submitting patches, helping us determine problems, our package maintainers and of course everybody who creates issues for us to fix. bpython-0.12/doc/sphinx/source/community.rst0000644000175000017500000000166412031025543021551 0ustar simonsimon00000000000000.. _community: Community ========= Do you need help with using bpython? Do you want to thank the contributors personally? Or maybe you want to help out, contribute some code or resources or want to help in making bpython known to other persons? These are the places where you can find us. IRC --- You can find us in #bpython on the Freenode network (irc.freenode.net). Don't worry when you get no response (this does not usually happen) but we are all from Europe and when you get to the channel during our nighttime you might have to wait a while for a response. Mailinglist ----------- We have a mailinglist at `google groups `_. You can post questions there and releases are announced on the mailing list. Website ------- Our main website is http://bpython-interpreter.org/, our documentation can be found at http://docs.bpython-interpreter.org/ and our pastebin can be found at http://bpaste.net/. bpython-0.12/doc/sphinx/source/man-bpython.rst0000644000175000017500000000677312061377417022004 0ustar simonsimon00000000000000:orphan: bpython manual page =================== Synopsis -------- **bpython** [*options*] [*file* [*args*]] **bpython-gtk** [*options*] [*file* [*args*]] **bpython-urwid** [*options*] [*file* [*args*]] Description ----------- The idea is to provide the user with all the features in-line, much like modern IDEs, but in a simple, lightweight package that can be run in a terminal window. In-line syntax highlighting. Hilights commands as you type! Readline-like autocomplete with suggestions displayed as you type. Press tab to complete expressions when there's only one suggestion. Expected parameter list. This displays a list of parameters for any function you call. It uses the inspect module, then tries pydoc. Rewind. This is a bit misleading, but it code that has been entered is remembered, and when you Rewind, it pops the last line and re\-evaluates the entire code. This is error\-prone, and mostly useful for defining classes and functions. Pastebin code/write to file. This posts the current buffer to a pastebin (paste.pocoo.org) or writes it to a file. Flush curses screen to stdout. Unlike other curses apps, bpython dumps the screen data to stdout when you quit, so you see what you've done in the buffer of your terminal. Options ------- The long and short forms of options, shown here as alternatives, are equivalent. If :program:`bpython` sees an argument it does not know, execution falls back to the regular Python interpreter. --config= Use instead of default config file. -h, --help Show the help message and exit. -i, --interactive Drop to bpython shell after running file instead of exiting. The PYTHONSTARTUP file is not read. -q, --quiet Do not flush the output to stdout. -V, --version Print :program:`bpython`'s version and exit. In addition to the above options, :program:`bpython-urwid` also supports the following options if Twisted is available: -r , --reactor= Use Twisted's instead of urwid's event loop. --help-reactors Display a list of available Twisted reactors. -p , --plugin= Execute a :program:`twistd` plugin. Use :program:`twistd` to get a list of available plugins. Use -- to pass options to the plugin. -s , --server= Run an eval server on port . This options forces the use of a Twisted reactor. :program:`bpython-gtk` also supports the following options: --socket-id= Embed bpython. Keys ---- :program:`bpython`'s keys are fully configurable. See http://docs.bpython-interpreter.org/configuration.html#keyboard Files ----- **$XDG_CONFIG_HOME/bpython/config** Your bpython config. See sample-config (in /usr/share/doc/bpython/examples on Debian) for various options you can use, or read :manpage:`python-config(5)`. Known bugs ---------- See http://bitbucket.org/bobf/bpython/issues/ for a list of known issues. See also -------- :manpage:`bpython-config(5)`, :manpage:`python(1)` Author ------ :program:`bpython` was written by Robert Anthony Farrell and his bunch of loyal followers. This manual page was written by Jørgen Pedersen Tjernø , for the Debian project (but may be used by others). bpython-0.12/doc/sphinx/source/configuration.rst0000644000175000017500000000071112061377417022401 0ustar simonsimon00000000000000.. _configuration: Configuration ============= You can copy the supplied sample-config to your home directory and move it to ``$XDG_CONFIG_HOME/bpython/config`` [#f1]_. bpython tries to find ``$XDG_CONFIG_HOME/bpython/config`` and use it as its configuration, if the file does not exist bpython will use its documented defaults. .. :: Footnotes .. [#f1] ``$XDG_CONFIG_HOME`` defaults to ``~/.config`` if not set. .. include:: configuration-options.rst bpython-0.12/doc/sphinx/source/bpaste.rst0000644000175000017500000000051512031025543020775 0ustar simonsimon00000000000000.. _bpaste: bpaste ====== bpaste (http://bpaste.net) is the pastebin which we run for bpython. bpython is configured by default to paste to this pastebin. Removal ------- If you want a paste removed from the pastebin you can email Simon at simon@ikanobori.jp and he will remove the paste for you, be sure to mention the paste URL. bpython-0.12/doc/sphinx/source/conf.py0000644000175000017500000001532512061377417020306 0ustar simonsimon00000000000000# -*- coding: utf-8 -*- # # bpython documentation build configuration file, created by # sphinx-quickstart on Mon Jun 8 11:58:16 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'bpython' copyright = u'2008-2012 Bob Farrell, Andreas Stuehrk et al.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = 'mercurial' # The full version, including alpha/beta/rc tags. release = 'mercurial' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. unused_docs = ['configuration-options'] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'bpythondoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'bpython.tex', u'bpython Documentation', u'Robert Farrell', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('man-bpython', 'bpython', u'a fancy {curses, GTK+, urwid} interface to the Python interactive interpreter', [], 1), ('man-bpython-config', 'bpython-config', u'user configuration file for bpython', [], 5) ] # If true, show URL addresses after external links. #man_show_urls = False bpython-0.12/doc/sphinx/source/logo.png0000644000175000017500000004534412031025543020444 0ustar simonsimon00000000000000PNG  IHDRXtEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp GXIDATx} WuU3ZG%Yd[^066^Y[$0’H{!!!>L30`+&/,kK{z{_ZnݺuH=3]vkr"@!}Hp |܅Dl{́% J#$x fmr^^c6zQgcr ĽBĮ$@۰`%XP`o"x=EhH$3Bί"$i9ɀ$eɗ2*gl~x#74[MwR$RjeR/_*O"BtCj5:b]jfg'ۀ`9-Y:j gm0(+,"پ^%; )^IR I{64?|X,Se J5A6>k08OtX/6h`\1Q6U&x.' 0ueH9 1|E湔L*S}Ƿ?*5HJUM:<p'8ܿ05W0{Gmr GD:I]GR҆ϒ}_wښd8z'am6p)O uMҠ3X.^Xa 0TJ#/|qt3/f dp pnϿ_z9Ӭ==!>:4l\7?W2ekG`,' R χre(k5& ĚͰ"m(X➇9s:!m[\ ]o*]^pL.*EjQ՛oժ|Qn6p8+5PkĽG/F0~pe'A$DNr:\5=>Et`@c@Z}Uo/]i1ZHG{ɢ\wd3AvB=OM n3軈yl:ABkS泎 +06c%mdo/>sNT:ߛ,] deAĺ:"=ןZT?|I~HYag_5FXE~1/+PU~n?c4<;cGfl6YvJmy'?@39e}ww@ppN9 %5%sfV(s'޵[^[XtltEo3c;4洓N;BDއo6cd:>bG</:$ݿ`v\$' sZw>\Jp)+Ȅx +TⰬY:_:>ѽ1S6@:|21Na0n/%0a^^t@2X9}K,si#$hVHX3G"  X0Pe/&dΣGlsd2`W+(!)$Rgi5`"s >9StFI27}S=~ rmf?T̡^:Yثn !afಽCЙ>n! Bם`M:s7w~>6h={:<zeؘW͛b*iZ^W+) Kk P.IDhJ 4p" ;bc2MɃPeֹ[szMEIIy'}@_x9?!$87›\! taW9I-xzݜ4PHJ* ~Z2L,:W[+"IvHZqT~`@N0^{x_Z@K_[, l̿yG{3 b ٟK"x4˘I{BoĉFWm^+^g?0tֿߖ?,:$uzIR@LF;cj3eY~BR8A]d v|dθdٙo|lACImf?&$ kw&K+Mۖv)Uȓ^N9\g\w\Ӻaܨ0g $e7~w?d8ryrd_~px(;j`.|DSsd_=eDLMEM>|<(+ח›,+I%"E`&_g[ 23.RMQpjs}x(z֝eiĒe>ek.@,̩$sT=WfvZeV-S{Jҹ\0D#wZԪ :q2},0~o|x :܀QNA=tM5HP`- '()OqYpEd^+q c@ڋO:눃 }le&rpBF^//F@LɌ &EAL-"8a f gA$D[o8;(3 "s{R9@Asr$!5$@j,ID uS]F;e>kx鲗^G꽠*ANjDX%'#T2&F!$|IA:`/N:q4U5BpV.hEm{2j{urv? zICwH{--QHgpi D0 1'I9{SJe,"1bxv[  ے%p:R9dbI9AdygXcD@@V7m<)._с,>~ @ЬT r3HMÞoLa"d14V?B!mX8>7 tSvuwOY s\IpG\VE {u?i ,q6iVN@XʸYA՝e|:A/řZ< /\k%wX[L:eFUaNf;axp(g^BAL ϛ;(G)B5RE$8zu.# 癶V9leZDc4{B;L[05 evbfd!O#@M8t$1/#m1ccC)*5iA0xHظ\ 茶8Yb!_ g1;D# uL>DKϼU\;ZZ: K5$_ IX1a^ ThllzkͮL,^!ybZmתZ,8r^)OcOkyVy0kֱ!@(-AS{P rYw]W 뻖V2NWX]v}m+-5BLGܽ{o/^W*]Չc?m6}tJ]y&} 9Y=&+N=n66q;1r*e5AP$?̋t's X8=A D(Բͧ X~hdynQj }Y𚤅pfPœ^'޹;QH&m@w0YGfu]`N6 #u8޺a4 IR-oZw%#4S?n0lM b VbY(!)8Hi!#{ r jtV.g势Ot A@To׼/->73^ D,,x";p~E{wf>K{U#Q$$8/YĈ/\?NL~+?.?eɩC %sz%:J"z$"Y8f2KԻ^*A6XʅЬ\A`C$$}DdtA! -kU^uy|ioIMrm`Aʂl IX0b {!+E|Cy!ǘK ʘ&fΩ m+,;ݟsB\/ 鱕6frADZ- 1y %b i*IuHC{۹MW~c~7fVtҴS`n wl`͚*Ͷһ#u Њw932ig)2I1%Z[~.1m.H c 3B҃b B $Kc$OťnTھ%vIʜv?8t٧Kގ%l 3u (WQHca 3(݉3݃KOC-^^bn,$0O$Ep_k" # vAi' :$܉v{je_%^p <( iN\ϱ\x+y'$!_BOWL!1 B 7b=~q 35t V]p%Br 9 &+&!k/1QĕMLX[~XSͶ&yt }+75vf NWYHzPLGxBD,kD"|xNeD9$i']75/8l@z޸UN{XQX$H<z>1wUþD;i'aN{x.!4QaFc+`Zĵi1 Q,yRSOlK= Dp3~6#A5-!&!$nIƐt{] nE^f?o&=HĊ @bij  wM ӆ+R'S}6V^dL+s~iYmK5$C[z܊O 1+@ɫ"M K0vI ?AEy)5qRRr~rp_+L$kA8 (ThhUjD%uH2lyDb3WcoL5Hx){.H [he#\]7R:Щ N?jk_]y5 @KzR AF-[Pʅ; u_Lc}<96 <:7 e9َZ;҇ݓh?gL1-_OiLߢS W.޸1?4R DGo#S5dgJ-hȜ"-#o3|%f0,%qc=fNQ)[#Y=(Jm=X ܴJR2U!YlGajĒxd5 :Fig瞅 7x&&I9˺#(0EPUZej#?9z_fwwtBS!5|E2*Ir12̊jṾxһ 9qאKZ $#u=/jQ2 -%O uqkڡ训6_[ᵯ!j?"v7}@2M,V=k5,s+:iB,!Zl ji{3&g)Cdu?p{4M@ĕmyPW3yԩcM7፛$17CB)9ctE6<$IJT$ $t\ŐhkZEհKIv,l sڣ̆:g[z Jy3l8u]dR))EҊd%$SB0CjR$lnֻ8Ґ(d@Cq}J?=ư`?/jՅ$bRBcUMkUM4Tũii~Yä]H$2No` yvM0{p㻖 /Җ/)KԁՕƹBRd>٦!xL&<"$K9\Xv639U+<{ܿߡ:Zky*3aF"86!g6E gk7gA1IH]PðD‹`B`k:T LYb &Ƨ=ѩs!M;__c\'bR!q|![8/뇥 044)H+G$A] a#b|p>8ߢWw➽>ߵF3]  c4ҁP'$A&iLMV4R5wdǖZlNN`h/˰<[d"aU,vB5Nc7Wn+؜L ,+nؖNY攓~dž~|M;ݷ~tQ`( m>&I T%R {$ul)~P{J6*z%Hd$^A!L.KPP&)?Z@!8ݟ10xZ@G0`ڐUZ{}޷?<-V>[<+ ,sOTaѯeN|-v[Hz,RPpLӲ *dqkE֟x`! Al_|imI#bjc]̛rW{>lQ~\QK-6yCB} &@4ok8P P8!U؉Kz'U9u$NTC\7@єkq;7-dk0+[tS|V 5uLBb?&Ry@cϖeLfL-tCOڋ587.;XP-3ZfbY iϜ,adșCd@0jAD0!{˫ՐS$>8DD% V/e.?I/:&LlStߍÎ>۳ 4ʊS# 1IVJa}u`t G H(}I?uҦ 8+9'J!AlIǜ$4%1X%aϐx!ـ@>Oub' \{/\*:lڿ~{ҋlqeZIB%昙UqeO|/II EiD؃"l^K\у^<|wU7gj52׸v1.|@9A!؉HEϐXf,هkE |3NykXotI*%3~ :i6/ E3BtA/%!@RB ϷCxNyvQ1洁C/>aʤv c nsYX%C`-"N01fϗ IB7p58۟No"wج;%<.QO1s Y>Hk!Aѩ(Tpm棈##' O^iQUH!s:SbO[I9mb!bv2ɢr|eXD9q>HlDҐ3tw &C`JT,7v{79ZAin; evJglAq^7?SLWKn8c̸A<,Y3Fl`$lO)ښMOvdmꖿTJEM+3ڏ8#lmWG}h14`zv9tɋO-V *>J0$̵glL3uI{(w~w!#%LR{[==f}5= KLr׿}I#LAL ~-bye/ǞeqϾ'*3iBFsbYn "4+i!ӶAm\_ h@ƞKrK/&O$"c5jȡ-1~]KO>?]w9`QmZ;!vA׆Ldh\oIm׺RXYW-cGa@²aWy *3EзnLITHbV\09,ȒHlr&},s޶>[I%߳lqsHe{rXRH H@О~Iڨ>Du6Yۯd%8#on3!ҹlj)0"F3YXӎ 5MdW5E<3۷dp][SjGH U+(MC;&{/;r} )sNI,LX#@0r0^9WŞD4GaC75% H޵wz&6Q`λvM5P48H 0H[ik3B+/mY><*Ao*7 #ԲҪj8Q 96!鴌BhB"ZBX ym0a7``\G8=ΌdRbF~ PL6+ɋӲ.TcDXsDA R!vt0aYV>,C 2?KP+Wj3E&zCc&`v%B$&NjOpX;Z?f$ț/Ei^ՎxD(g#$%3#{zJ4tRk 8I)HQn. %AD D$)@Ċ r%yٜ>{ojʼnfVY BYRUڍtݳ1Ϊ*Vr '%`^cNqf!|MLeI±~_?e!(37)xùqr"O .UwptAs R!6( I +ΌW?Ze ܂'r21!S4+r$LI$Q0CGD? Q.Z?~6(2&VG%$ڙ;Q*X*Nk>0 rEBģQHLv3j{=/NˁŚWIR߅O[2$SP  xbRl$\aӉI5!#|a p}>UީAuo2=VY8 yլD1Ԗ86 NHdT*>2^+_W4:Ϥn9&(G\%Ӎn#%MIWwfg-8Mlݸ%*98Z*Uժ^rғ6"V򮉽/µʴ-e  cRUZ#0mIݫ>´)'1nYUǕOZZI!& 3t'"ٮ/leD5,)Hbs*aD,Y,I'O: 3LȍqϽ|u.eفJ( ZcL[P=}|D.$I`3ǐۂCMĩ,% IQ6%0h1Zezf\ؕmÛ?|jS&rTq$Dv0MD&Pϼ=ןa(5 {,%+j6(@aದk4pu4lܔu;ԣ|F9~ f Y6OA2PXPg#qݮ d>ghǡ#f>~=]G:h3>qw燣ڭ+##q&f2nGjM}X\oso5otj%PyU4xtJ<EۡWuf 5ɬVJd ܶgfˆSщPz4c!"8>͈mK|C=o *V Qqo[ӱ®rfl^1jKeZ#s)~ Ǯ|y_EmbZSu\ti:/MP6%5,!O!IJyM;؏ Z;[f5-0d$Nqq^"u䳩ܛJe̩P5SSB 4Nt2 q(u", {K-IOGD e%%acK{66!瞟M$Jbjbj Wv1Pę1T$b.=Fc.p\@&1FM5ڡޒ>WC 7s "al^9Tj ,uhi!h "IpIb|$o CCc*9# UDBAag,[whH LXfSNyTO>U#gƏi,dg%pytHk+޽4v[Q1{+zٰDm07x Gٰ Xy>ɽZw4yoiz苕>±mҔbLb6%I3#~p@=/4@ 4H(8(a;iS-s^ s6ܳ*4M,>H#( J|B,A0[f3m?;JCN2OW( ' -6锄B͟fb[Xj$ex#/OڢD8zmqӝ4`EWv!J2{=#>?=M Nt O%O("Sޢ>d`Տ~m}q!vヴ'R#3 ;r[OP@(:M(K_|g"ƞ'k2u$FXߑ$j.(dYc|.IOVMQJhO~;rˮ;mp(X* 1?JN9sS EImaVsARЫ3~|O?.A;;2W$tRl0wxe9U`O{k^<9Zg$x;>~3;l 3(rѫ;ihj-_H&++.1H֊tH\j$ Gǿ;~ym{(p!»{31C  fLiT}<%ٴ$3 FbkFXylutoiwz⯿cpysqyi³AӒ$~?Y7nhd3)90̛4*V*LdEU;=}W6 PD^N~Hp& X1hCLƐ"jr`İU(BPpɒ&%$Կ>\ٶkj{OomO_54hMuX 4:Q৻l639]%Ӫ:Q ,QD2^%ds=@(2=̼yD|oze5EE-9!O xOZ|x^\.j3ӵ xefcc[g4NaۖMgoX! V8 $wt\7eV3 XEyc۔Yod&{aditneI*r- Dp|6mx\i1KJ y 8*=XGg&ƙ ; h=AI~ ‚˘ 0Di_^EIҥwӘmqxw& }|&% ̡"Wή.,Irְݻ ްQհﵺ_͘U֩!-ԁlaœ_MOJM:kL/ Z<$;7Bh.%{ߑ>K -(| PD(\jժ>mx,#]eFT.!ev [ǰ ,D-gXF&3 1Sb:i߿c~8ѬQ vz@gЛJ]O.sye> YЉJxS*6>/$s6f В+>̰ij-=!BH=9k:=vnu>8(hD$InB4pU-E,3agMu6SyEDI$+|h[vPRUq>02nH#' q5Gbqگʎ3AkyH ՀS>[&MCwȼNNQ$|b%j)Ow$ >HgW K k_wˢF\ߋkX7 h ȼ3H;1u< XZRj0@'2h> ȂfI@jH85V,ݐ3c,'_r|!.8 oqrz9&;|/ib%!1CbrOP6+C=uף{ #W}Xo}zhşo/t)f͠k0 &Ai!/|>H-a[=pG~=g&݈UL[/f$3gSҠHP!҂SMdX$0S+7j85=fǸu_o;>pĸ768iv2w1%" LPsཱfr|ػZ5RbO}[Պ+1k|Rl=5䴷.5t 8 V"mV!D0v Ƕ'Kx., jԴVoBb 2"W @+{#RIrpʓblG춵y &Pvy|D)曳}h=~EFAbIsO~qʼְqzrzT?YCIOEB@cЅ$G9$(`@@e.]װ4Pz]|oԼz\o :5NBU?dłluMl' %#8iea%nurUP٬<m T nbҁ;s*)o#T5 =s`E.Zkϴ*Sp(>_:]lʚ%i@yezu;,M/AfSx<^sp۱ۥ\C09.e`,oW]xeo t+LDxuZ%WڤAx%=_9葝@FNP;]f-c6*%˶< .{;C5WKE)#g9`7O9r:8:4$ݗ:hg|vo|^wIEhވIbե]/n3wW*ኊzR,/5*+pz4Sï*xЈqReh\ϣ?{w֬;lF _'d'O;&ju\8{$ k8{n54rx: v3`ljqUvt<󅛟7R:f4tГգܲe`9if4 j8Igode=8vښCԴf^iYG{/nyf*:wdٸ>N5QC5¹H!?X*} gvajڧw߹6IEM8FV8Βk}c[w4a;ayEӵů!Q%N"@᎟q5e yE?;xOwf[2GI ⑟ͣ'⛇OFBFjv_?ķk*+u~HҶ̡a= h;?xW}#QK k)ooB%.* [LhGm1hyLX\a^a^@:LCt2-Rẫ׽3971tz/g[ Q8I {hduϭx2sB gnDf@R= WN:ם2 0 ]K:|sd~Oȷ~1[KL1kC*y: q]>'_$+(w r_˗,/lͼ6]jD'p"Nk@0_iRhmW[Q+N֦tTvP5lb xP8s0H@ad(NYS̫̭ӊl",eq**K4c HRg邱IN iZOk:&5&ĶSvBl-m]9_ct&b@‚_Y(\Č"F@R@$l--j5n[TB2o<:ټ >t꼝~6pF4a6X`Ɓֹz+y1, ߫18A8 q^yEFi '#1000' (so +1) for x in range(1000 - 4 - 1): self.history.forward() self.assertEqual(self.history.forward(), '#999') def test_append(self): self.history.append('print "foo\n"\n') self.history.append('\n') self.assertEqual(self.history.back(), 'print "foo\n"') def test_enter(self): self.history.enter('#lastnumber!') self.assertEqual(self.history.back(), '#999') self.assertEqual(self.history.forward(), '#lastnumber!') def test_reset(self): self.history.enter('#lastnumber!') self.history.reset() self.assertEqual(self.history.back(), '#999') self.assertEqual(self.history.forward(), '') class TestMatchesIterator(unittest.TestCase): def setUp(self): self.matches = ['bobby', 'bobbies', 'bobberina'] self.matches_iterator = repl.MatchesIterator(current_word='bob', matches=self.matches) def test_next(self): self.assertEqual(self.matches_iterator.next(), self.matches[0]) for x in range(len(self.matches) - 1): self.matches_iterator.next() self.assertEqual(self.matches_iterator.next(), self.matches[0]) self.assertEqual(self.matches_iterator.next(), self. matches[1]) self.assertNotEqual(self.matches_iterator.next(), self.matches[1]) def test_previous(self): self.assertEqual(self.matches_iterator.previous(), self.matches[2]) for x in range(len(self.matches) - 1): self.matches_iterator.previous() self.assertNotEqual(self.matches_iterator.previous(), self.matches[0]) self.assertEqual(self.matches_iterator.previous(), self.matches[1]) self.assertEqual(self.matches_iterator.previous(), self.matches[0]) def test_nonzero(self): """self.matches_iterator should be False at start, then True once we active a match. """ self.assertFalse(self.matches_iterator) self.matches_iterator.next() self.assertTrue(self.matches_iterator) def test_iter(self): slice = islice(self.matches_iterator, 0, 9) self.assertEqual(list(slice), self.matches * 3) def test_current(self): self.assertRaises(ValueError, self.matches_iterator.current) self.matches_iterator.next() self.assertEqual(self.matches_iterator.current(), self.matches[0]) def test_update(self): slice = islice(self.matches_iterator, 0, 3) self.assertEqual(list(slice), self.matches) newmatches = ['string', 'str', 'set'] self.matches_iterator.update('s', newmatches) newslice = islice(newmatches, 0, 3) self.assertNotEqual(list(slice), self.matches) self.assertEqual(list(newslice), newmatches) class TestArgspec(unittest.TestCase): def setUp(self): self.repl = FakeRepl() self.repl.push("def spam(a, b, c):\n", False) self.repl.push(" pass\n", False) self.repl.push("\n", False) def setInputLine(self, line): """Set current input line of the test REPL.""" self.repl.input_line = line def test_func_name(self): for (line, expected_name) in [("spam(", "spam"), ("spam(map([]", "map"), ("spam((), ", "spam")]: self.setInputLine(line) self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.current_func.__name__, expected_name) def test_syntax_error_parens(self): for line in ["spam(]", "spam([)", "spam())"]: self.setInputLine(line) # Should not explode self.repl.get_args() def test_kw_arg_position(self): self.setInputLine("spam(a=0") self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.argspec[3], "a") self.setInputLine("spam(1, b=1") self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.argspec[3], "b") self.setInputLine("spam(1, c=2") self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.argspec[3], "c") def test_lambda_position(self): self.setInputLine("spam(lambda a, b: 1, ") self.assertTrue(self.repl.get_args()) self.assertTrue(self.repl.argspec) # Argument position self.assertEqual(self.repl.argspec[3], 1) def test_issue127(self): self.setInputLine("x=range(") self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.current_func.__name__, "range") self.setInputLine("{x:range(") self.assertTrue(self.repl.get_args()) self.assertEqual(self.repl.current_func.__name__, "range") self.setInputLine("foo(1, 2, x,range(") self.assertEqual(self.repl.current_func.__name__, "range") self.setInputLine("(x,range(") self.assertEqual(self.repl.current_func.__name__, "range") def test_nonexistent_name(self): self.setInputLine("spamspamspam(") self.assertFalse(self.repl.get_args()) class TestRepl(unittest.TestCase): def setUp(self): self.repl = FakeRepl() def test_current_string(self): self.repl.input_line = 'a = "2"' self.assertEqual(self.repl.current_string(), '"2"') self.repl.input_line = 'a = "2" + 2' self.assertEqual(self.repl.current_string(), '') # TODO: figure out how to capture whether foobar is in globals @skip('not working yet') def test_push(self): self.repl = FakeRepl() self.repl.push("foobar = 2") self.repl.push("\"foobar\" in globals().keys()") # COMPLETE TESTS # 1. Global tests def test_simple_global_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SIMPLE}) self.repl.input_line = "d" self.repl.current_word = "d" self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['def', 'del', 'delattr(', 'dict(', 'dir(', 'divmod(']) def test_substring_global_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SUBSTRING}) self.repl.input_line = "time" self.repl.current_word = "time" self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['RuntimeError(', 'RuntimeWarning(']) def test_fuzzy_global_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.FUZZY}) self.repl.input_line = "doc" self.repl.current_word = "doc" self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['UnboundLocalError(', '__doc__']) # 2. Attribute tests def test_simple_attribute_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SIMPLE}) self.repl.input_line = "Foo.b" self.repl.current_word = "Foo.b" code = "class Foo():\n\tdef bar(self):\n\t\tpass\n" for line in code.split("\n"): self.repl.push(line) self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['Foo.bar']) def test_substring_attribute_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SUBSTRING}) self.repl.input_line = "Foo.az" self.repl.current_word = "Foo.az" code = "class Foo():\n\tdef baz(self):\n\t\tpass\n" for line in code.split("\n"): self.repl.push(line) self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['Foo.baz']) def test_fuzzy_attribute_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.FUZZY}) self.repl.input_line = "Foo.br" self.repl.current_word = "Foo.br" code = "class Foo():\n\tdef bar(self):\n\t\tpass\n" for line in code.split("\n"): self.repl.push(line) self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['Foo.bar']) # 3. Edge Cases def test_updating_namespace_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SIMPLE}) self.repl.input_line = "foo" self.repl.current_word = "foo" self.repl.push("foobar = 2") self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertEqual(self.repl.completer.matches, ['foobar']) def test_file_should_not_appear_in_complete(self): self.repl = FakeRepl({'autocomplete_mode': autocomplete.SIMPLE}) self.repl.input_line = "_" self.repl.current_word = "_" self.assertTrue(self.repl.complete()) self.assertTrue(hasattr(self.repl.completer,'matches')) self.assertTrue('__file__' not in self.repl.completer.matches) class TestCliRepl(unittest.TestCase): def setUp(self): self.repl = FakeCliRepl() def test_atbol(self): self.assertTrue(self.repl.atbol()) self.repl.s = "\t\t" self.assertTrue(self.repl.atbol()) self.repl.s = "\t\tnot an empty line" self.assertFalse(self.repl.atbol()) def test_addstr(self): self.repl.complete = Mock(True) self.repl.s = "foo" self.repl.addstr("bar") self.assertEqual(self.repl.s, "foobar") self.repl.cpos = 3 self.repl.addstr('buzz') self.assertEqual(self.repl.s, "foobuzzbar") def test_cw(self): self.repl.cpos = 2 self.assertEqual(self.repl.cw(), None) self.repl.cpos = 0 self.repl.s = '' self.assertEqual(self.repl.cw(), None) self.repl.s = "this.is.a.test\t" self.assertEqual(self.repl.cw(), None) s = "this.is.a.test" self.repl.s = s self.assertEqual(self.repl.cw(), s) s = "\t\tthis.is.a.test" self.repl.s = s self.assertEqual(self.repl.cw(), s.lstrip()) self.repl.s = "import datetime" self.assertEqual(self.repl.cw(), 'datetime') class TestCliReplTab(unittest.TestCase): def setUp(self): def setup_matches(tab=False): if self.repl.cw() and len(self.repl.cw().split('.')) == 1: self.repl.matches = ["foobar", "foofoobar"] else: self.repl.matches = ["Foo.foobar", "Foo.foofoobar"] self.repl.matches_iter = repl.MatchesIterator() self.repl.matches_iter.update(self.repl.cw(), self.repl.matches) self.repl = FakeCliRepl() # Stub out CLIRepl attributes self.repl.buffer = [] self.repl.argspec = Mock() self.repl.print_line = Mock() self.repl.show_list = Mock() # Stub out complete self.repl.complete = Mock() self.repl.complete.return_value = True self.repl.complete.side_effect = setup_matches self.repl.matches_iter = None # Stub out the config logic self.repl.config = Mock() self.repl.config.tab_length = 4 self.repl.config.auto_display_list = True self.repl.config.list_win_visible = True self.repl.config.autocomplete_mode = autocomplete.SIMPLE # 3 Types of tab complete def test_simple_tab_complete(self): self.repl.s = "foo" self.repl.tab() self.assertEqual(self.repl.s, "foobar") def test_substring_tab_complete(self): self.repl.s = "bar" self.repl.config.autocomplete_mode = autocomplete.FUZZY self.repl.tab() self.assertEqual(self.repl.s, "foobar") self.repl.tab() self.assertEqual(self.repl.s, "foofoobar") def test_fuzzy_tab_complete(self): self.repl.s = "br" self.repl.config.autocomplete_mode = autocomplete.FUZZY self.repl.tab() self.assertEqual(self.repl.s, "foobar") # Edge Cases def test_normal_tab(self): """make sure pressing the tab key will still in some cases add a tab""" self.repl.s = "" self.repl.tab() self.assertEqual(self.repl.s, " ") def test_back_parameter(self): self.repl.s = "foo" self.repl.tab(back=True) self.assertEqual(self.repl.s, "foofoobar") def test_nth_forward(self): """make sure that pressing tab twice will fist expand and then cycle to the first match""" self.repl.s = "f" self.repl.tab() self.repl.tab() self.assertEqual(self.repl.s, "foobar") def test_current_word(self): """Complete should not be affected by words that precede it.""" self.repl.s = "import f" self.repl.tab() self.assertEqual(self.repl.s, "import foo") self.repl.tab() self.assertEqual(self.repl.s, "import foobar") self.repl.tab() self.assertEqual(self.repl.s, "import foofoobar") # Attribute Tests def test_fuzzy_attribute_tab_complete(self): """Test fuzzy attribute with no text""" self.repl.s = "Foo." self.repl.config.autocomplete_mode = autocomplete.FUZZY self.repl.tab() self.assertEqual(self.repl.s, "Foo.foobar") def test_fuzzy_attribute_tab_complete2(self): """Test fuzzy attribute with some text""" self.repl.s = "Foo.br" self.repl.config.autocomplete_mode = autocomplete.FUZZY self.repl.tab() self.assertEqual(self.repl.s, "Foo.foobar") # Expand Tests def test_simple_expand(self): self.repl.s = "f" self.repl.tab() self.assertEqual(self.repl.s, "foo") def test_substring_expand_forward(self): self.repl.config.autocomplete_mode = autocomplete.SUBSTRING self.repl.s = "ba" self.repl.tab() self.assertEqual(self.repl.s, "bar") def test_fuzzy_expand(self): pass if __name__ == '__main__': unittest.main() bpython-0.12/bpython/test/test_crashers.py0000644000175000017500000000665412035242140021304 0ustar simonsimon00000000000000import fcntl import os import pty import struct import sys import termios import textwrap import unittest try: from twisted.internet import reactor from twisted.internet.defer import Deferred from twisted.internet.protocol import ProcessProtocol from twisted.trial.unittest import TestCase as TrialTestCase except ImportError: reactor = None TEST_CONFIG = os.path.join(os.path.dirname(__file__), "test.config") def set_win_size(fd, rows, columns): s = struct.pack('HHHH', rows, columns, 0, 0) fcntl.ioctl(fd, termios.TIOCSWINSZ, s) class CrashersTest(object): backend = "cli" def run_bpython(self, input): """ Run bpython (with `backend` as backend) in a subprocess and enter the given input. Uses a test config that disables the paste detection. Retuns bpython's output. """ result = Deferred() class Protocol(ProcessProtocol): STATES = (SEND_INPUT, COLLECT) = xrange(2) def __init__(self): self.data = "" self.delayed_call = None self.states = iter(self.STATES) self.state = self.states.next() def outReceived(self, data): self.data += data if self.delayed_call is not None: self.delayed_call.cancel() self.delayed_call = reactor.callLater(0.5, self.next) def next(self): self.delayed_call = None if self.state == self.SEND_INPUT: index = self.data.find(">>> ") if index >= 0: self.data = self.data[index + 4:] self.transport.write(input) self.state = self.states.next() else: self.transport.closeStdin() if self.transport.pid is not None: self.delayed_call = None self.transport.signalProcess("TERM") def processExited(self, reason): if self.delayed_call is not None: self.delayed_call.cancel() result.callback(self.data) (master, slave) = pty.openpty() set_win_size(slave, 25, 80) reactor.spawnProcess(Protocol(), sys.executable, (sys.executable, "-m", "bpython." + self.backend, "--config", TEST_CONFIG), env=dict(TERM="vt100", LANG=os.environ.get("LANG", "")), usePTY=(master, slave, os.ttyname(slave))) return result def test_issue108(self): input = textwrap.dedent( """\ def spam(): u"y\\xe4y" \b spam(""") deferred = self.run_bpython(input) return deferred.addCallback(self.check_no_traceback) def test_issue133(self): input = textwrap.dedent( """\ def spam(a, (b, c)): pass \b spam(1""") return self.run_bpython(input).addCallback(self.check_no_traceback) def check_no_traceback(self, data): tb = data[data.find("Traceback"):] self.assertTrue("Traceback" not in data, tb) if reactor is not None: class CursesCrashersTest(TrialTestCase, CrashersTest): backend = "cli" class UrwidCrashersTest(TrialTestCase, CrashersTest): backend = "urwid" if __name__ == "__main__": unittest.main() bpython-0.12/bpython/test/test_importcompletion.py0000644000175000017500000000000012031025543023053 0ustar simonsimon00000000000000bpython-0.12/bpython/test/test_inspection.py0000644000175000017500000000356512035242140021643 0ustar simonsimon00000000000000import unittest from bpython import inspection class TestInspection(unittest.TestCase): def test_is_callable(self): class OldCallable: def __call__(self): pass class Callable(object): def __call__(self): pass class OldNoncallable: pass class Noncallable(object): pass def spam(): pass self.assertTrue(inspection.is_callable(spam)) self.assertTrue(inspection.is_callable(Callable)) self.assertTrue(inspection.is_callable(Callable())) self.assertTrue(inspection.is_callable(OldCallable)) self.assertTrue(inspection.is_callable(OldCallable())) self.assertFalse(inspection.is_callable(Noncallable())) self.assertFalse(inspection.is_callable(OldNoncallable())) self.assertFalse(inspection.is_callable(None)) def test_parsekeywordpairs(self): # See issue #109 def fails(spam=['-a', '-b']): pass default_arg_repr = "['-a', '-b']" self.assertEqual(str(['-a', '-b']), default_arg_repr, 'This test is broken (repr does not match), fix me.') argspec = inspection.getargspec('fails', fails) defaults = argspec[1][3] self.assertEqual(str(defaults[0]), default_arg_repr) def test_pasekeywordpairs_string(self): def spam(eggs="foo, bar"): pass defaults = inspection.getargspec("spam", spam)[1][3] self.assertEqual(repr(defaults[0]), "'foo, bar'") def test_parsekeywordpairs_multiple_keywords(self): def spam(eggs=23, foobar="yay"): pass defaults = inspection.getargspec("spam", spam)[1][3] self.assertEqual(repr(defaults[0]), "23") self.assertEqual(repr(defaults[1]), "'yay'") if __name__ == '__main__': unittest.main() bpython-0.12/bpython/test/__init__.py0000644000175000017500000000000012031025543020147 0ustar simonsimon00000000000000bpython-0.12/bpython/_py3compat.py0000644000175000017500000000304612035242140017522 0ustar simonsimon00000000000000# encoding: utf-8 # The MIT License # # Copyright (c) 2012 the bpython authors. # # 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. # """ Helper module for Python 3 compatibility. Defines the following attributes: - PythonLexer: Pygment's Python lexer matching the hosting runtime's Python version. - py3: True if the hosting Python runtime is of Python version 3 or later """ import sys py3 = (sys.version_info[0] == 3) if py3: from pygments.lexers import Python3Lexer as PythonLexer else: from pygments.lexers import PythonLexer bpython-0.12/bpython/args.py0000644000175000017500000001013212061377441016406 0ustar simonsimon00000000000000""" Module to handle command line argument parsing, for all front-ends. """ from __future__ import with_statement import os import sys import code from optparse import OptionParser, OptionGroup from bpython import __version__ from bpython.config import default_config_path, loadini, Struct from bpython.translations import _ class OptionParserFailed(ValueError): """Raised by the RaisingOptionParser for a bogus commandline.""" class RaisingOptionParser(OptionParser): def error(self, msg): raise OptionParserFailed() def parse(args, extras=None, ignore_stdin=False): """Receive an argument list - if None, use sys.argv - parse all args and take appropriate action. Also receive optional extra options: this should be a tuple of (title, description, options) title: The title for the option group description: A full description of the option group options: A list of optparse.Option objects to be added to the group e.g.: parse(['-i', '-m', 'foo.py'], ('Front end-specific options', 'A full description of what these options are for', [optparse.Option('-f', action='store_true', dest='f', help='Explode'), optparse.Option('-l', action='store_true', dest='l', help='Love')])) Return a tuple of (config, options, exec_args) wherein "config" is the config object either parsed from a default/specified config file or default config options, "options" is the parsed options from OptionParser.parse_args, and "exec_args" are the args (if any) to be parsed to the executed file (if any). """ if args is None: args = sys.argv[1:] parser = RaisingOptionParser( usage=_('Usage: %prog [options] [file [args]]\n' 'NOTE: If bpython sees an argument it does ' 'not know, execution falls back to the ' 'regular Python interpreter.')) # This is not sufficient if bpython gains its own -m support # (instead of falling back to Python itself for that). # That's probably fixable though, for example by having that # option swallow all remaining arguments in a callback. parser.disable_interspersed_args() parser.add_option('--config', default=default_config_path(), help=_('Use CONFIG instead of default config file.')) parser.add_option('--interactive', '-i', action='store_true', help=_('Drop to bpython shell after running file ' 'instead of exiting.')) parser.add_option('--quiet', '-q', action='store_true', help=_("Don't flush the output to stdout.")) parser.add_option('--version', '-V', action='store_true', help=_('Print version and exit.')) if extras is not None: extras_group = OptionGroup(parser, extras[0], extras[1]) for option in extras[2]: extras_group.add_option(option) parser.add_option_group(extras_group) try: options, args = parser.parse_args(args) except OptionParserFailed: # Just let Python handle this os.execv(sys.executable, [sys.executable] + args) if options.version: print 'bpython version', __version__, print 'on top of Python', sys.version.split()[0] print ('(C) 2008-2012 Bob Farrell, Andreas Stuehrk et al. ' 'See AUTHORS for detail.') raise SystemExit if not ignore_stdin and not (sys.stdin.isatty() and sys.stdout.isatty()): interpreter = code.InteractiveInterpreter() interpreter.runsource(sys.stdin.read()) raise SystemExit config = Struct() loadini(config, options.config) return config, options, args def exec_code(interpreter, args): """ Helper to execute code in a given interpreter. args should be a [faked] sys.argv """ with open(args[0], 'r') as sourcefile: code_obj = compile(sourcefile.read(), args[0], 'exec') old_argv, sys.argv = sys.argv, args sys.path.insert(0, os.path.abspath(os.path.dirname(args[0]))) interpreter.runcode(code_obj) sys.argv = old_argv bpython-0.12/bpython/inspection.py0000644000175000017500000002115712035242140017622 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2011 the bpython authors. # # 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. # from __future__ import with_statement import collections import inspect import keyword import pydoc import re import types from pygments.token import Token from bpython._py3compat import PythonLexer, py3 try: collections.Callable has_collections_callable = True except AttributeError: has_collections_callable = False try: types.InstanceType has_instance_type = True except AttributeError: has_instance_type = False if not py3: _name = re.compile(r'[a-zA-Z_]\w*$') class AttrCleaner(object): """A context manager that tries to make an object not exhibit side-effects on attribute lookup.""" def __init__(self, obj): self.obj = obj def __enter__(self): """Try to make an object not exhibit side-effects on attribute lookup.""" type_ = type(self.obj) __getattribute__ = None __getattr__ = None # Dark magic: # If __getattribute__ doesn't exist on the class and __getattr__ does # then __getattr__ will be called when doing # getattr(type_, '__getattribute__', None) # so we need to first remove the __getattr__, then the # __getattribute__, then look up the attributes and then restore the # original methods. :-( # The upshot being that introspecting on an object to display its # attributes will avoid unwanted side-effects. if py3 or type_ != types.InstanceType: __getattr__ = getattr(type_, '__getattr__', None) if __getattr__ is not None: try: setattr(type_, '__getattr__', (lambda *_, **__: None)) except TypeError: __getattr__ = None __getattribute__ = getattr(type_, '__getattribute__', None) if __getattribute__ is not None: try: setattr(type_, '__getattribute__', object.__getattribute__) except TypeError: # XXX: This happens for e.g. built-in types __getattribute__ = None self.attribs = (__getattribute__, __getattr__) # /Dark magic def __exit__(self, exc_type, exc_val, exc_tb): """Restore an object's magic methods.""" type_ = type(self.obj) __getattribute__, __getattr__ = self.attribs # Dark magic: if __getattribute__ is not None: setattr(type_, '__getattribute__', __getattribute__) if __getattr__ is not None: setattr(type_, '__getattr__', __getattr__) # /Dark magic class _Repr(object): """ Helper for `fixlongargs()`: Returns the given value in `__repr__()`. """ def __init__(self, value): self.value = value def __repr__(self): return self.value __str__ = __repr__ def parsekeywordpairs(signature): tokens = PythonLexer().get_tokens(signature) preamble = True stack = [] substack = [] parendepth = 0 for token, value in tokens: if preamble: if token is Token.Punctuation and value == u"(": preamble = False continue if token is Token.Punctuation: if value in [u'(', u'{', u'[']: parendepth += 1 elif value in [u')', u'}', u']']: parendepth -= 1 elif value == ':' and parendepth == -1: # End of signature reached break if ((value == ',' and parendepth == 0) or (value == ')' and parendepth == -1)): stack.append(substack) substack = [] continue if value and (parendepth > 0 or value.strip()): substack.append(value) d = {} for item in stack: if len(item) >= 3: d[item[0]] = ''.join(item[2:]) return d def fixlongargs(f, argspec): """Functions taking default arguments that are references to other objects whose str() is too big will cause breakage, so we swap out the object itself with the name it was referenced with in the source by parsing the source itself !""" if argspec[3] is None: # No keyword args, no need to do anything return values = list(argspec[3]) if not values: return keys = argspec[0][-len(values):] try: src = inspect.getsourcelines(f) except (IOError, IndexError): # IndexError is raised in inspect.findsource(), can happen in # some situations. See issue #94. return signature = ''.join(src[0]) kwparsed = parsekeywordpairs(signature) for i, (key, value) in enumerate(zip(keys, values)): if len(repr(value)) != len(kwparsed[key]): values[i] = _Repr(kwparsed[key]) argspec[3] = values def getpydocspec(f, func): try: argspec = pydoc.getdoc(f) except NameError: return None rx = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*?)\((.*?)\)') s = rx.search(argspec) if s is None: return None if not hasattr(f, '__name__') or s.groups()[0] != f.__name__: return None args = list() defaults = list() varargs = varkwargs = None kwonly_args = list() kwonly_defaults = dict() for arg in s.group(2).split(','): arg = arg.strip() if arg.startswith('**'): varkwargs = arg[2:] elif arg.startswith('*'): varargs = arg[1:] else: arg, _, default = arg.partition('=') if varargs is not None: kwonly_args.append(arg) if default: kwonly_defaults[arg] = default else: args.append(arg) if default: defaults.append(default) return [func, (args, varargs, varkwargs, defaults, kwonly_args, kwonly_defaults)] def getargspec(func, f): # Check if it's a real bound method or if it's implicitly calling __init__ # (i.e. FooClass(...) and not FooClass.__init__(...) -- the former would # not take 'self', the latter would: func_name = getattr(f, '__name__', None) try: is_bound_method = ((inspect.ismethod(f) and f.im_self is not None) or (func_name == '__init__' and not func.endswith('.__init__'))) except: # if f is a method from a xmlrpclib.Server instance, func_name == # '__init__' throws xmlrpclib.Fault (see #202) return None try: if py3: argspec = inspect.getfullargspec(f) else: argspec = inspect.getargspec(f) argspec = list(argspec) fixlongargs(f, argspec) argspec = [func, argspec, is_bound_method] except (TypeError, KeyError): with AttrCleaner(f): argspec = getpydocspec(f, func) if argspec is None: return None if inspect.ismethoddescriptor(f): argspec[1][0].insert(0, 'obj') argspec.append(is_bound_method) return argspec def is_eval_safe_name(string): if py3: return all(part.isidentifier() and not keyword.iskeyword(part) for part in string.split('.')) else: return all(_name.match(part) and not keyword.iskeyword(part) for part in string.split('.')) def is_callable(obj): if has_instance_type and isinstance(obj, types.InstanceType): # Work around a CPython bug, see CPython issue #7624 return callable(obj) elif has_collections_callable: return isinstance(obj, collections.Callable) else: return callable(obj) bpython-0.12/bpython/translations/0000755000175000017500000000000012061403162017612 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/it_IT/0000755000175000017500000000000012061403162020622 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/it_IT/LC_MESSAGES/0000755000175000017500000000000012061403162022407 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/it_IT/LC_MESSAGES/bpython.po0000644000175000017500000000765312031025543024445 0ustar simonsimon00000000000000# Italian (Italy) translations for bpython. # Copyright (C) 2010 bpython developers # This file is distributed under the same license as the bpython project. # Michele Orrù , 2010. # msgid "" msgstr "" "Project-Id-Version: bpython 0.9.7\n" "Report-Msgid-Bugs-To: http://bitbucket.org/bobf/bpython/issues\n" "POT-Creation-Date: 2012-07-16 12:50+0200\n" "PO-Revision-Date: 2010-07-21 13:53+0200\n" "Last-Translator: Michele Orrù\n" "Language-Team: Michele Orrù\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: bpython/args.py:53 msgid "" "Usage: %prog [options] [file [args]]\n" "NOTE: If bpython sees an argument it does not know, execution falls back " "to the regular Python interpreter." msgstr "" #: bpython/args.py:63 msgid "Use CONFIG instead of default config file." msgstr "" #: bpython/args.py:65 msgid "Drop to bpython shell after running file instead of exiting." msgstr "" #: bpython/args.py:68 msgid "Don't flush the output to stdout." msgstr "" #: bpython/args.py:70 msgid "Print version and exit." msgstr "" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "y" msgstr "s" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "yes" msgstr "si" #: bpython/cli.py:970 bpython/gtk_.py:484 msgid "Cannot show source." msgstr "Non è possibile mostrare il codice sorgente" #: bpython/cli.py:1642 bpython/urwid.py:612 #, python-format msgid " <%s> Rewind <%s> Save <%s> Pastebin <%s> Pager <%s> Show Source " msgstr " <%s> Rewind <%s> Salva <%s> Pastebin <%s> Pager <%s> Mostra Sorgente" #: bpython/gtk_.py:92 bpython/gtk_.py:117 msgid "An error occurred." msgstr "È stato riscontrato un errore" #: bpython/gtk_.py:99 msgid "Exception details" msgstr "Dettagli sull'eccezione" #: bpython/gtk_.py:151 msgid "Statusbar" msgstr "Barra di stato" #: bpython/gtk_.py:228 msgid "tooltip" msgstr "tooltip" #: bpython/gtk_.py:297 msgid "File to save to" msgstr "File nel quale salvare" #: bpython/gtk_.py:308 msgid "Python files" msgstr "Files python" #: bpython/gtk_.py:313 msgid "All files" msgstr "Tutti i files" #: bpython/gtk_.py:771 msgid "gtk-specific options" msgstr "Opzioni specifiche di gtk" #: bpython/gtk_.py:772 msgid "Options specific to bpython's Gtk+ front end" msgstr "Opzioni specifiche riguardo il frontend gtk+ di bpython" #: bpython/gtk_.py:774 msgid "Embed bpython" msgstr "" #: bpython/gtk_.py:830 msgid "Pastebin selection" msgstr "" #: bpython/repl.py:776 msgid "Pastebin buffer? (y/N) " msgstr "" #: bpython/repl.py:777 msgid "Pastebin aborted" msgstr "" #: bpython/repl.py:784 #, python-format msgid "Duplicate pastebin. Previous URL: %s" msgstr "" #: bpython/repl.py:798 #, python-format msgid "Pastebin error for URL '%s': %s" msgstr "" #: bpython/repl.py:802 bpython/repl.py:821 msgid "Posting data to pastebin..." msgstr "" #: bpython/repl.py:807 #, python-format msgid "Upload failed: %s" msgstr "" #: bpython/repl.py:816 bpython/repl.py:860 #, python-format msgid "Pastebin URL: %s" msgstr "" #: bpython/repl.py:833 msgid "Upload failed: Helper program not found." msgstr "" #: bpython/repl.py:836 msgid "Upload failed: Helper program could not be run." msgstr "" #: bpython/repl.py:843 #, python-format msgid "Upload failed: Helper program returned non-zero exit status %s." msgstr "" #: bpython/repl.py:847 msgid "Upload failed: No output from helper program." msgstr "" #: bpython/repl.py:854 msgid "Upload failed: Failed to recognize the helper program's output as an URL." msgstr "" #: bpython/urwid.py:1108 msgid "Run a reactor (see --help-reactors)." msgstr "" #: bpython/urwid.py:1110 msgid "List available reactors for -r." msgstr "" #: bpython/urwid.py:1112 msgid "" "twistd plugin to run (use twistd for a list). Use \"--\" to pass further " "options to the plugin." msgstr "" #: bpython/urwid.py:1115 msgid "Port to run an eval server on (forces Twisted)." msgstr "" bpython-0.12/bpython/translations/es_ES/0000755000175000017500000000000012061403162020610 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/es_ES/LC_MESSAGES/0000755000175000017500000000000012061403162022375 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/es_ES/LC_MESSAGES/bpython.po0000644000175000017500000000744012031025543024425 0ustar simonsimon00000000000000# Spanish (Spain) translations for bpython. # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the bpython project. # Claudia Medde, 2010. # msgid "" msgstr "" "Project-Id-Version: bpython 0.9.7\n" "Report-Msgid-Bugs-To: http://bitbucket.org/bobf/bpython/issues\n" "POT-Creation-Date: 2012-07-16 12:50+0200\n" "PO-Revision-Date: 2010-07-21 14:46+0200\n" "Last-Translator: Claudia Medde\n" "Language-Team: bpython developers\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: bpython/args.py:53 msgid "" "Usage: %prog [options] [file [args]]\n" "NOTE: If bpython sees an argument it does not know, execution falls back " "to the regular Python interpreter." msgstr "" #: bpython/args.py:63 msgid "Use CONFIG instead of default config file." msgstr "" #: bpython/args.py:65 msgid "Drop to bpython shell after running file instead of exiting." msgstr "" #: bpython/args.py:68 msgid "Don't flush the output to stdout." msgstr "" #: bpython/args.py:70 msgid "Print version and exit." msgstr "" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "y" msgstr "s" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "yes" msgstr "si" #: bpython/cli.py:970 bpython/gtk_.py:484 msgid "Cannot show source." msgstr "Imposible mostrar el código fuente" #: bpython/cli.py:1642 bpython/urwid.py:612 #, python-format msgid " <%s> Rewind <%s> Save <%s> Pastebin <%s> Pager <%s> Show Source " msgstr "" " <%s> Rewind <%s> Salva <%s> Pastebin <%s> Pager <%s> Mostra el " "código fuente" #: bpython/gtk_.py:92 bpython/gtk_.py:117 msgid "An error occurred." msgstr "" #: bpython/gtk_.py:99 msgid "Exception details" msgstr "" #: bpython/gtk_.py:151 msgid "Statusbar" msgstr "Statusbar" #: bpython/gtk_.py:228 msgid "tooltip" msgstr "tooltip" #: bpython/gtk_.py:297 msgid "File to save to" msgstr "" #: bpython/gtk_.py:308 msgid "Python files" msgstr "Files Python" #: bpython/gtk_.py:313 msgid "All files" msgstr "Todos los files" #: bpython/gtk_.py:771 msgid "gtk-specific options" msgstr "" #: bpython/gtk_.py:772 msgid "Options specific to bpython's Gtk+ front end" msgstr "" #: bpython/gtk_.py:774 msgid "Embed bpython" msgstr "Embed bpython" #: bpython/gtk_.py:830 msgid "Pastebin selection" msgstr "Pastebin la selección" #: bpython/repl.py:776 msgid "Pastebin buffer? (y/N) " msgstr "" #: bpython/repl.py:777 msgid "Pastebin aborted" msgstr "" #: bpython/repl.py:784 #, python-format msgid "Duplicate pastebin. Previous URL: %s" msgstr "" #: bpython/repl.py:798 #, python-format msgid "Pastebin error for URL '%s': %s" msgstr "" #: bpython/repl.py:802 bpython/repl.py:821 msgid "Posting data to pastebin..." msgstr "" #: bpython/repl.py:807 #, python-format msgid "Upload failed: %s" msgstr "" #: bpython/repl.py:816 bpython/repl.py:860 #, python-format msgid "Pastebin URL: %s" msgstr "" #: bpython/repl.py:833 msgid "Upload failed: Helper program not found." msgstr "" #: bpython/repl.py:836 msgid "Upload failed: Helper program could not be run." msgstr "" #: bpython/repl.py:843 #, python-format msgid "Upload failed: Helper program returned non-zero exit status %s." msgstr "" #: bpython/repl.py:847 msgid "Upload failed: No output from helper program." msgstr "" #: bpython/repl.py:854 msgid "Upload failed: Failed to recognize the helper program's output as an URL." msgstr "" #: bpython/urwid.py:1108 msgid "Run a reactor (see --help-reactors)." msgstr "" #: bpython/urwid.py:1110 msgid "List available reactors for -r." msgstr "" #: bpython/urwid.py:1112 msgid "" "twistd plugin to run (use twistd for a list). Use \"--\" to pass further " "options to the plugin." msgstr "" #: bpython/urwid.py:1115 msgid "Port to run an eval server on (forces Twisted)." msgstr "" bpython-0.12/bpython/translations/de/0000755000175000017500000000000012061403162020202 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/de/LC_MESSAGES/0000755000175000017500000000000012061403162021767 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/de/LC_MESSAGES/bpython.po0000644000175000017500000000760012031025543024015 0ustar simonsimon00000000000000# German translations for bpython. # Copyright (C) 2012 bpython developers # This file is distributed under the same license as the bpython project. # Sebastian Ramacher , 2012. # msgid "" msgstr "" "Project-Id-Version: bpython mercurial\n" "Report-Msgid-Bugs-To: http://bitbucket.org/bobf/bpython/issues\n" "POT-Creation-Date: 2012-07-16 12:50+0200\n" "PO-Revision-Date: 2012-04-05 09:37+0200\n" "Last-Translator: Sebastian Ramacher \n" "Language-Team: de \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: bpython/args.py:53 msgid "" "Usage: %prog [options] [file [args]]\n" "NOTE: If bpython sees an argument it does not know, execution falls back " "to the regular Python interpreter." msgstr "" #: bpython/args.py:63 msgid "Use CONFIG instead of default config file." msgstr "Verwende CONFIG antatt der standardmäßigen Konfigurationsdatei." #: bpython/args.py:65 msgid "Drop to bpython shell after running file instead of exiting." msgstr "" #: bpython/args.py:68 msgid "Don't flush the output to stdout." msgstr "" #: bpython/args.py:70 msgid "Print version and exit." msgstr "Zeige Versionsinformationen an und beende." #: bpython/cli.py:303 bpython/urwid.py:550 msgid "y" msgstr "j" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "yes" msgstr "ja" #: bpython/cli.py:970 bpython/gtk_.py:484 msgid "Cannot show source." msgstr "Kann Quellcode nicht anzeigen." #: bpython/cli.py:1642 bpython/urwid.py:612 #, python-format msgid " <%s> Rewind <%s> Save <%s> Pastebin <%s> Pager <%s> Show Source " msgstr "" #: bpython/gtk_.py:92 bpython/gtk_.py:117 msgid "An error occurred." msgstr "Ein Fehler ist aufgetreten." #: bpython/gtk_.py:99 msgid "Exception details" msgstr "Ausnahmedetails" #: bpython/gtk_.py:151 msgid "Statusbar" msgstr "Statusleiste" #: bpython/gtk_.py:228 msgid "tooltip" msgstr "" #: bpython/gtk_.py:297 msgid "File to save to" msgstr "" #: bpython/gtk_.py:308 msgid "Python files" msgstr "Python Dateien" #: bpython/gtk_.py:313 msgid "All files" msgstr "Alle Dateien" #: bpython/gtk_.py:771 msgid "gtk-specific options" msgstr "" #: bpython/gtk_.py:772 msgid "Options specific to bpython's Gtk+ front end" msgstr "" #: bpython/gtk_.py:774 msgid "Embed bpython" msgstr "Bette bpython ein" #: bpython/gtk_.py:830 msgid "Pastebin selection" msgstr "" #: bpython/repl.py:776 msgid "Pastebin buffer? (y/N) " msgstr "" #: bpython/repl.py:777 msgid "Pastebin aborted" msgstr "" #: bpython/repl.py:784 #, python-format msgid "Duplicate pastebin. Previous URL: %s" msgstr "" #: bpython/repl.py:798 #, python-format msgid "Pastebin error for URL '%s': %s" msgstr "" #: bpython/repl.py:802 bpython/repl.py:821 msgid "Posting data to pastebin..." msgstr "" #: bpython/repl.py:807 #, python-format msgid "Upload failed: %s" msgstr "" #: bpython/repl.py:816 bpython/repl.py:860 #, python-format msgid "Pastebin URL: %s" msgstr "" #: bpython/repl.py:833 msgid "Upload failed: Helper program not found." msgstr "" #: bpython/repl.py:836 msgid "Upload failed: Helper program could not be run." msgstr "" #: bpython/repl.py:843 #, python-format msgid "Upload failed: Helper program returned non-zero exit status %s." msgstr "" #: bpython/repl.py:847 msgid "Upload failed: No output from helper program." msgstr "" #: bpython/repl.py:854 msgid "Upload failed: Failed to recognize the helper program's output as an URL." msgstr "" #: bpython/urwid.py:1108 msgid "Run a reactor (see --help-reactors)." msgstr "" #: bpython/urwid.py:1110 msgid "List available reactors for -r." msgstr "" #: bpython/urwid.py:1112 msgid "" "twistd plugin to run (use twistd for a list). Use \"--\" to pass further " "options to the plugin." msgstr "" #: bpython/urwid.py:1115 msgid "Port to run an eval server on (forces Twisted)." msgstr "" bpython-0.12/bpython/translations/__init__.py0000644000175000017500000000171112031025543021723 0ustar simonsimon00000000000000import gettext import locale import os.path import sys from bpython import package_dir translator = None if sys.version_info >= (3, 0): def _(message): return translator.gettext(message) else: def _(message): return translator.ugettext(message) def init(locale_dir=None, languages=None): try: locale.setlocale(locale.LC_ALL, '') except locale.Error: # This means that the user's environment is broken. Let's just continue # with the default C locale. sys.stderr.write("Error: Your locale settings are not supported by " "the system. Using the fallback 'C' locale instead. " "Please fix your locale settings.\n") global translator if locale_dir is None: locale_dir = os.path.join(package_dir, 'translations') translator = gettext.translation('bpython', locale_dir, languages, fallback=True) bpython-0.12/bpython/translations/nl_NL/0000755000175000017500000000000012061403162020614 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/nl_NL/LC_MESSAGES/0000755000175000017500000000000012061403162022401 5ustar simonsimon00000000000000bpython-0.12/bpython/translations/nl_NL/LC_MESSAGES/bpython.po0000644000175000017500000000764012031025543024433 0ustar simonsimon00000000000000# Dutch (Netherlands) translations for bpython. # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the bpython project. # Simon de Vlieger, 2011 . # msgid "" msgstr "" "Project-Id-Version: bpython 0.9.7.1\n" "Report-Msgid-Bugs-To: http://bitbucket.org/bobf/bpython/issues\n" "POT-Creation-Date: 2012-07-16 12:50+0200\n" "PO-Revision-Date: 2011-04-21 00:46+0100\n" "Last-Translator: Simon de Vlieger \n" "Language-Team: bpython developers\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" #: bpython/args.py:53 msgid "" "Usage: %prog [options] [file [args]]\n" "NOTE: If bpython sees an argument it does not know, execution falls back " "to the regular Python interpreter." msgstr "" #: bpython/args.py:63 msgid "Use CONFIG instead of default config file." msgstr "" #: bpython/args.py:65 msgid "Drop to bpython shell after running file instead of exiting." msgstr "" #: bpython/args.py:68 msgid "Don't flush the output to stdout." msgstr "" #: bpython/args.py:70 msgid "Print version and exit." msgstr "" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "y" msgstr "j" #: bpython/cli.py:303 bpython/urwid.py:550 msgid "yes" msgstr "ja" #: bpython/cli.py:970 bpython/gtk_.py:484 msgid "Cannot show source." msgstr "Kan de broncode niet laden" #: bpython/cli.py:1642 bpython/urwid.py:612 #, python-format msgid " <%s> Rewind <%s> Save <%s> Pastebin <%s> Pager <%s> Show Source " msgstr " <%s> Rewind <%s> Opslaan <%s> Pastebin <%s> Pager <%s> Toon broncode" #: bpython/gtk_.py:92 bpython/gtk_.py:117 msgid "An error occurred." msgstr "Er is een fout opgetreden" #: bpython/gtk_.py:99 msgid "Exception details" msgstr "Fout details" #: bpython/gtk_.py:151 msgid "Statusbar" msgstr "Statusbalk" #: bpython/gtk_.py:228 msgid "tooltip" msgstr "tooltip" #: bpython/gtk_.py:297 msgid "File to save to" msgstr "Bestandsnaaam" #: bpython/gtk_.py:308 msgid "Python files" msgstr "Python bestanden" #: bpython/gtk_.py:313 msgid "All files" msgstr "Alle bestanden" #: bpython/gtk_.py:771 msgid "gtk-specific options" msgstr "gtk-specifieke opties" #: bpython/gtk_.py:772 msgid "Options specific to bpython's Gtk+ front end" msgstr "Opties specifiek voor bpythons Gtk+ front end" #: bpython/gtk_.py:774 msgid "Embed bpython" msgstr "Embed bpython" #: bpython/gtk_.py:830 msgid "Pastebin selection" msgstr "Pastebin de selectie" #: bpython/repl.py:776 msgid "Pastebin buffer? (y/N) " msgstr "" #: bpython/repl.py:777 msgid "Pastebin aborted" msgstr "" #: bpython/repl.py:784 #, python-format msgid "Duplicate pastebin. Previous URL: %s" msgstr "" #: bpython/repl.py:798 #, python-format msgid "Pastebin error for URL '%s': %s" msgstr "" #: bpython/repl.py:802 bpython/repl.py:821 msgid "Posting data to pastebin..." msgstr "" #: bpython/repl.py:807 #, python-format msgid "Upload failed: %s" msgstr "" #: bpython/repl.py:816 bpython/repl.py:860 #, python-format msgid "Pastebin URL: %s" msgstr "" #: bpython/repl.py:833 msgid "Upload failed: Helper program not found." msgstr "" #: bpython/repl.py:836 msgid "Upload failed: Helper program could not be run." msgstr "" #: bpython/repl.py:843 #, python-format msgid "Upload failed: Helper program returned non-zero exit status %s." msgstr "" #: bpython/repl.py:847 msgid "Upload failed: No output from helper program." msgstr "" #: bpython/repl.py:854 msgid "Upload failed: Failed to recognize the helper program's output as an URL." msgstr "" #: bpython/urwid.py:1108 msgid "Run a reactor (see --help-reactors)." msgstr "" #: bpython/urwid.py:1110 msgid "List available reactors for -r." msgstr "" #: bpython/urwid.py:1112 msgid "" "twistd plugin to run (use twistd for a list). Use \"--\" to pass further " "options to the plugin." msgstr "" #: bpython/urwid.py:1115 msgid "Port to run an eval server on (forces Twisted)." msgstr "" bpython-0.12/bpython/formatter.py0000644000175000017500000000724412031025543017455 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Bob Farrell # # 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. # A simple formatter for bpython to work with Pygments. # Pygments really kicks ass, it made it really easy to # get the exact behaviour I wanted, thanks Pygments.:) from pygments.formatter import Formatter from pygments.token import Keyword, Name, Comment, String, Error, \ Number, Operator, Token, Whitespace, Literal, Punctuation """These format strings are pretty ugly. \x01 represents a colour marker, which can be proceded by one or two of the following letters: k, r, g, y, b, m, c, w, d Which represent: blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default e.g. \x01y for yellow, \x01gb for green on blue background \x02 represents the bold attribute \x03 represents the start of the actual text that is output (in this case it's a %s for substitution) \x04 represents the end of the string; this is necessary because the strings are all joined together at the end so the parser needs them as delimeters """ Parenthesis = Token.Punctuation.Parenthesis theme_map = { Keyword: 'keyword', Name: 'name', Comment: 'comment', String: 'string', Literal: 'string', Error: 'error', Number: 'number', Token.Literal.Number.Float: 'number', Operator: 'operator', Punctuation: 'punctuation', Token: 'token', Whitespace: 'background', Parenthesis: 'paren', Parenthesis.UnderCursor: 'operator'} class BPythonFormatter(Formatter): """This is the custom formatter for bpython. Its format() method receives the tokensource and outfile params passed to it from the Pygments highlight() method and slops them into the appropriate format string as defined above, then writes to the outfile object the final formatted string. See the Pygments source for more info; it's pretty straightforward.""" def __init__(self, color_scheme, **options): self.f_strings = {} for k, v in theme_map.iteritems(): self.f_strings[k] = '\x01%s' % (color_scheme[v],) if k is Parenthesis: # FIXME: Find a way to make this the inverse of the current # background colour self.f_strings[k] += 'I' Formatter.__init__(self, **options) def format(self, tokensource, outfile): o = '' for token, text in tokensource: if text == '\n': continue while token not in self.f_strings: token = token.parent o += "%s\x03%s\x04" % (self.f_strings[token], text) outfile.write(o.rstrip()) # vim: sw=4 ts=4 sts=4 ai et bpython-0.12/bpython/config.py0000644000175000017500000002423612035242140016715 0ustar simonsimon00000000000000from __future__ import with_statement import os import sys from ConfigParser import ConfigParser from itertools import chain from bpython.keys import cli_key_dispatch as key_dispatch from bpython.autocomplete import SIMPLE as default_completion MAGIC_METHODS = ", ".join("__%s__" % s for s in [ "init", "repr", "str", "lt", "le", "eq", "ne", "gt", "ge", "cmp", "hash", "nonzero", "unicode", "getattr", "setattr", "get", "set","call", "len", "getitem", "setitem", "iter", "reversed", "contains", "add", "sub", "mul", "floordiv", "mod", "divmod", "pow", "lshift", "rshift", "and", "xor", "or", "div", "truediv", "neg", "pos", "abs", "invert", "complex", "int", "float", "oct", "hex", "index", "coerce", "enter", "exit"] ) class Struct(object): """Simple class for instantiating objects we can add arbitrary attributes to and use for various arbitrary things.""" def get_config_home(): """Returns the base directory for bpython's configuration files.""" xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '~/.config') return os.path.join(xdg_config_home, 'bpython') def default_config_path(): """Returns bpython's default configuration file path.""" return os.path.join(get_config_home(), 'config') def fill_config_with_default_values(config, default_values): for section in default_values.iterkeys(): if not config.has_section(section): config.add_section(section) for (opt, val) in default_values[section].iteritems(): if not config.has_option(section, opt): config.set(section, opt, str(val)) def loadini(struct, configfile): """Loads .ini configuration file and stores its values in struct""" config_path = os.path.expanduser(configfile) if not os.path.isfile(config_path) and configfile == default_config_path(): # We decided that '~/.bpython/config' still was a crappy # place, use XDG Base Directory Specification instead. Fall # back to old config, though. config_path = os.path.expanduser('~/.bpython/config') config = ConfigParser() fill_config_with_default_values(config, { 'general': { 'arg_spec': True, 'auto_display_list': True, 'color_scheme': 'default', 'complete_magic_methods' : True, 'magic_methods' : MAGIC_METHODS, 'autocomplete_mode': default_completion, 'dedent_after': 1, 'flush_output': True, 'highlight_show_source': True, 'hist_file': '~/.pythonhist', 'hist_length': 100, 'hist_duplicates': True, 'paste_time': 0.02, 'syntax': True, 'tab_length': 4, 'pastebin_confirm': True, 'pastebin_private': False, 'pastebin_url': 'http://bpaste.net/xmlrpc/', 'pastebin_private': True, 'pastebin_show_url': 'http://bpaste.net/show/$paste_id/', 'pastebin_helper': '', }, 'keyboard': { 'clear_line': 'C-u', 'clear_screen': 'C-l', 'clear_word': 'C-w', 'cut_to_buffer': 'C-k', 'delete': 'C-d', 'down_one_line': 'C-n', 'exit': '', 'last_output': 'F9', 'pastebin': 'F8', 'save': 'C-s', 'show_source': 'F2', 'suspend': 'C-z', 'undo': 'C-r', 'search': 'C-o', 'up_one_line': 'C-p', 'yank_from_buffer': 'C-y'}, 'cli': { 'suggestion_width': 0.8, 'trim_prompts': False, }, 'gtk': { 'font': 'monospace 10', 'color_scheme': 'default'}}) if not config.read(config_path): # No config file. If the user has it in the old place then complain if os.path.isfile(os.path.expanduser('~/.bpython.ini')): sys.stderr.write("Error: It seems that you have a config file at " "~/.bpython.ini. Please move your config file to " "%s\n" % default_config_path()) sys.exit(1) struct.dedent_after = config.getint('general', 'dedent_after') struct.tab_length = config.getint('general', 'tab_length') struct.auto_display_list = config.getboolean('general', 'auto_display_list') struct.syntax = config.getboolean('general', 'syntax') struct.arg_spec = config.getboolean('general', 'arg_spec') struct.paste_time = config.getfloat('general', 'paste_time') struct.highlight_show_source = config.getboolean('general', 'highlight_show_source') struct.hist_file = config.get('general', 'hist_file') struct.hist_length = config.getint('general', 'hist_length') struct.hist_duplicates = config.getboolean('general', 'hist_duplicates') struct.flush_output = config.getboolean('general', 'flush_output') struct.pastebin_key = config.get('keyboard', 'pastebin') struct.save_key = config.get('keyboard', 'save') struct.search_key = config.get('keyboard', 'search') struct.show_source_key = config.get('keyboard', 'show_source') struct.suspend_key = config.get('keyboard', 'suspend') struct.undo_key = config.get('keyboard', 'undo') struct.up_one_line_key = config.get('keyboard', 'up_one_line') struct.down_one_line_key = config.get('keyboard', 'down_one_line') struct.cut_to_buffer_key = config.get('keyboard', 'cut_to_buffer') struct.yank_from_buffer_key = config.get('keyboard', 'yank_from_buffer') struct.clear_word_key = config.get('keyboard', 'clear_word') struct.clear_line_key = config.get('keyboard', 'clear_line') struct.clear_screen_key = config.get('keyboard', 'clear_screen') struct.delete_key = config.get('keyboard', 'delete') struct.exit_key = config.get('keyboard', 'exit') struct.last_output_key = config.get('keyboard', 'last_output') struct.pastebin_confirm = config.getboolean('general', 'pastebin_confirm') struct.pastebin_private = config.getboolean('general', 'pastebin_private') struct.pastebin_url = config.get('general', 'pastebin_url') struct.pastebin_private = config.get('general', 'pastebin_private') struct.pastebin_show_url = config.get('general', 'pastebin_show_url') struct.pastebin_helper = config.get('general', 'pastebin_helper') struct.cli_suggestion_width = config.getfloat('cli', 'suggestion_width') struct.cli_trim_prompts = config.getboolean('cli', 'trim_prompts') struct.complete_magic_methods = config.getboolean('general', 'complete_magic_methods') methods = config.get('general', 'magic_methods') struct.magic_methods = [meth.strip() for meth in methods.split(",")] struct.autocomplete_mode = config.get('general', 'autocomplete_mode') struct.gtk_font = config.get('gtk', 'font') color_scheme_name = config.get('general', 'color_scheme') color_gtk_scheme_name = config.get('gtk', 'color_scheme') default_colors = { 'keyword': 'y', 'name': 'c', 'comment': 'b', 'string': 'm', 'error': 'r', 'number': 'G', 'operator': 'Y', 'punctuation': 'y', 'token': 'C', 'background': 'd', 'output': 'w', 'main': 'c', 'paren': 'R', 'prompt': 'c', 'prompt_more': 'g', } default_gtk_colors = { 'keyword': 'b', 'name': 'k', 'comment': 'b', 'string': 'm', 'error': 'r', 'number': 'G', 'operator': 'B', 'punctuation': 'g', 'token': 'C', 'background': 'w', 'output': 'k', 'main': 'c', 'paren': 'R', 'prompt': 'b', 'prompt_more': 'g', } if color_scheme_name == 'default': struct.color_scheme = default_colors else: struct.color_scheme = dict() theme_filename = color_scheme_name + '.theme' path = os.path.expanduser(os.path.join(get_config_home(), theme_filename)) old_path = os.path.expanduser(os.path.join('~/.bpython', theme_filename)) for path in [path, old_path]: try: load_theme(struct, path, struct.color_scheme, default_colors) except EnvironmentError: continue else: break else: sys.stderr.write("Could not load theme '%s'.\n" % (color_scheme_name, )) sys.exit(1) if color_gtk_scheme_name == 'default': struct.color_gtk_scheme = default_gtk_colors else: struct.color_gtk_scheme = dict() # Note: This is a new config option, hence we don't have a # fallback directory. path = os.path.expanduser(os.path.join(get_config_home(), color_gtk_scheme_name + '.theme')) try: load_theme(struct, path, struct.color_gtk_scheme, default_colors) except EnvironmentError: sys.stderr.write("Could not load gtk theme '%s'.\n" % (color_gtk_scheme_name, )) sys.exit(1) # checks for valid key configuration this part still sucks for key in (struct.pastebin_key, struct.save_key): key_dispatch[key] def load_theme(struct, path, colors, default_colors): theme = ConfigParser() with open(path, 'r') as f: theme.readfp(f) for k, v in chain(theme.items('syntax'), theme.items('interface')): if theme.has_option('syntax', k): colors[k] = theme.get('syntax', k) else: colors[k] = theme.get('interface', k) # Check against default theme to see if all values are defined for k, v in default_colors.iteritems(): if k not in colors: colors[k] = v f.close() bpython-0.12/bpython/repl.py0000644000175000017500000011312412035242140016405 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2011 the bpython authors. # # 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. # from __future__ import with_statement import code import codecs import errno import inspect import os import pydoc import subprocess import sys import textwrap import traceback import unicodedata from glob import glob from itertools import takewhile from locale import getpreferredencoding from socket import error as SocketError from string import Template from urllib import quote as urlquote from urlparse import urlparse from xmlrpclib import ServerProxy, Error as XMLRPCError from pygments.token import Token from bpython import importcompletion, inspection from bpython._py3compat import PythonLexer, py3 from bpython.formatter import Parenthesis from bpython.translations import _ from bpython.autocomplete import Autocomplete # Needed for special handling of __abstractmethods__ # abc only exists since 2.6, so check both that it exists and that it's # the one we're expecting try: import abc abc.ABCMeta has_abc = True except (ImportError, AttributeError): has_abc = False class Interpreter(code.InteractiveInterpreter): def __init__(self, locals=None, encoding=None): """The syntaxerror callback can be set at any time and will be called on a caught syntax error. The purpose for this in bpython is so that the repl can be instantiated after the interpreter (which it necessarily must be with the current factoring) and then an exception callback can be added to the Interpeter instance afterwards - more specifically, this is so that autoindentation does not occur after a traceback.""" self.encoding = encoding or sys.getdefaultencoding() self.syntaxerror_callback = None # Unfortunately code.InteractiveInterpreter is a classic class, so no super() code.InteractiveInterpreter.__init__(self, locals) if not py3: def runsource(self, source, filename='', symbol='single', encode=True): if encode: source = '# coding: %s\n%s' % (self.encoding, source.encode(self.encoding)) return code.InteractiveInterpreter.runsource(self, source, filename, symbol) def showsyntaxerror(self, filename=None): """Override the regular handler, the code's copied and pasted from code.py, as per showtraceback, but with the syntaxerror callback called and the text in a pretty colour.""" if self.syntaxerror_callback is not None: self.syntaxerror_callback() type, value, sys.last_traceback = sys.exc_info() sys.last_type = type sys.last_value = value if filename and type is SyntaxError: # Work hard to stuff the correct filename in the exception try: msg, (dummy_filename, lineno, offset, line) = value.args except: # Not the format we expect; leave it alone pass else: # Stuff in the right filename and right lineno if not py3: lineno -= 1 value = SyntaxError(msg, (filename, lineno, offset, line)) sys.last_value = value list = traceback.format_exception_only(type, value) self.writetb(list) def showtraceback(self): """This needs to override the default traceback thing so it can put it into a pretty colour and maybe other stuff, I don't know""" try: t, v, tb = sys.exc_info() sys.last_type = t sys.last_value = v sys.last_traceback = tb tblist = traceback.extract_tb(tb) del tblist[:1] # Set the right lineno (encoding header adds an extra line) if not py3: for i, (filename, lineno, module, something) in enumerate(tblist): if filename == '': tblist[i] = (filename, lineno - 1, module, something) l = traceback.format_list(tblist) if l: l.insert(0, "Traceback (most recent call last):\n") l[len(l):] = traceback.format_exception_only(t, v) finally: tblist = tb = None self.writetb(l) def writetb(self, lines): """This outputs the traceback and should be overridden for anything fancy.""" for line in lines: self.write(line) class History(object): def __init__(self, entries=None, duplicates=False): if entries is None: self.entries = [''] else: self.entries = list(entries) self.index = 0 self.saved_line = '' self.duplicates = duplicates def append(self, line): line = line.rstrip('\n') if line: if not self.duplicates: # remove duplicates try: while True: self.entries.remove(line) except ValueError: pass self.entries.append(line) def first(self): """Move back to the beginning of the history.""" if not self.is_at_end: self.index = len(self.entries) return self.entries[-self.index] def back(self, start=True, search=False): """Move one step back in the history.""" if not self.is_at_end: if search: self.index += self.find_partial_match_backward(self.saved_line) elif start: self.index += self.find_match_backward(self.saved_line) else: self.index += 1 return self.entries[-self.index] if self.index else self.saved_line def find_match_backward(self, search_term): filtered_list_len = len(self.entries) - self.index for idx, val in enumerate(reversed(self.entries[:filtered_list_len])): if val.startswith(search_term): return idx + 1 return 0 def find_partial_match_backward(self, search_term): filtered_list_len = len(self.entries) - self.index for idx, val in enumerate(reversed(self.entries[:filtered_list_len])): if search_term in val: return idx + 1 return 0 def forward(self, start=True, search=False): """Move one step forward in the history.""" if self.index > 1: if search: self.index -= self.find_partial_match_forward(self.saved_line) elif start: self.index -= self.find_match_forward(self.saved_line) else: self.index -= 1 return self.entries[-self.index] if self.index else self.saved_line else: self.index = 0 return self.saved_line def find_match_forward(self, search_term): filtered_list_len = len(self.entries) - self.index + 1 for idx, val in enumerate(self.entries[filtered_list_len:]): if val.startswith(search_term): return idx + 1 return self.index def find_partial_match_forward(self, search_term): filtered_list_len = len(self.entries) - self.index + 1 for idx, val in enumerate(self.entries[filtered_list_len:]): if search_term in val: return idx + 1 return self.index def last(self): """Move forward to the end of the history.""" if not self.is_at_start: self.index = 0 return self.entries[0] @property def is_at_end(self): return self.index >= len(self.entries) or self.index == -1 @property def is_at_start(self): return self.index == 0 def enter(self, line): if self.index == 0: self.saved_line = line @classmethod def from_filename(cls, filename): history = cls() history.load(filename) return history def load(self, filename, encoding): with codecs.open(filename, 'r', encoding, 'ignore') as hfile: for line in hfile: self.append(line) def reset(self): self.index = 0 self.saved_line = '' def save(self, filename, encoding, lines=0): with codecs.open(filename, 'w', encoding, 'ignore') as hfile: for line in self.entries[-lines:]: hfile.write(line) hfile.write('\n') class MatchesIterator(object): def __init__(self, current_word='', matches=[]): self.current_word = current_word self.matches = list(matches) self.index = -1 def __nonzero__(self): return self.index != -1 def __iter__(self): return self def current(self): if self.index == -1: raise ValueError('No current match.') return self.matches[self.index] def next(self): self.index = (self.index + 1) % len(self.matches) return self.matches[self.index] def previous(self): if self.index <= 0: self.index = len(self.matches) self.index -= 1 return self.matches[self.index] def update(self, current_word='', matches=[]): if current_word != self.current_word: self.current_word = current_word self.matches = list(matches) self.index = -1 class Interaction(object): def __init__(self, config, statusbar=None): self.config = config if statusbar: self.statusbar = statusbar def confirm(self, s): raise NotImplementedError def notify(self, s, n=10): raise NotImplementedError def file_prompt(self, s): raise NotImplementedError class Repl(object): """Implements the necessary guff for a Python-repl-alike interface The execution of the code entered and all that stuff was taken from the Python code module, I had to copy it instead of inheriting it, I can't remember why. The rest of the stuff is basically what makes it fancy. It reads what you type, passes it to a lexer and highlighter which returns a formatted string. This then gets passed to echo() which parses that string and prints to the curses screen in appropriate colours and/or bold attribute. The Repl class also keeps two stacks of lines that the user has typed in: One to be used for the undo feature. I am not happy with the way this works. The only way I have been able to think of is to keep the code that's been typed in in memory and re-evaluate it in its entirety for each "undo" operation. Obviously this means some operations could be extremely slow. I'm not even by any means certain that this truly represents a genuine "undo" implementation, but it does seem to be generally pretty effective. If anyone has any suggestions for how this could be improved, I'd be happy to hear them and implement it/accept a patch. I researched a bit into the idea of keeping the entire Python state in memory, but this really seems very difficult (I believe it may actually be impossible to work) and has its own problems too. The other stack is for keeping a history for pressing the up/down keys to go back and forth between lines. XXX Subclasses should implement echo, current_line, cw """ def __init__(self, interp, config): """Initialise the repl. interp is a Python code.InteractiveInterpreter instance config is a populated bpython.config.Struct. """ self.config = config self.cut_buffer = '' self.buffer = [] self.interp = interp self.interp.syntaxerror_callback = self.clear_current_line self.match = False self.rl_history = History(duplicates=config.hist_duplicates) self.s_hist = [] self.history = [] self.evaluating = False self.completer = Autocomplete(self.interp.locals, config) self.matches = [] self.matches_iter = MatchesIterator() self.argspec = None self.current_func = None self.highlighted_paren = None self.list_win_visible = False self._C = {} self.prev_block_finished = 0 self.interact = Interaction(self.config) self.ps1 = '>>> ' self.ps2 = '... ' # previous pastebin content to prevent duplicate pastes, filled on call # to repl.pastebin self.prev_pastebin_content = '' self.prev_pastebin_url = '' # Necessary to fix mercurial.ui.ui expecting sys.stderr to have this # attribute self.closed = False pythonhist = os.path.expanduser(self.config.hist_file) if os.path.exists(pythonhist): self.rl_history.load(pythonhist, getpreferredencoding() or "ascii") def startup(self): """ Execute PYTHONSTARTUP file if it exits. Call this after front end-specific initialisation. """ filename = os.environ.get('PYTHONSTARTUP') if filename and os.path.isfile(filename): with open(filename, 'r') as f: if py3: self.interp.runsource(f.read(), filename, 'exec') else: self.interp.runsource(f.read(), filename, 'exec', encode=False) def current_string(self, concatenate=False): """If the line ends in a string get it, otherwise return ''""" tokens = self.tokenize(self.current_line()) string_tokens = list(takewhile(token_is_any_of([Token.String, Token.Text]), reversed(tokens))) if not string_tokens: return '' opening = string_tokens.pop()[1] string = list() for (token, value) in reversed(string_tokens): if token is Token.Text: continue elif opening is None: opening = value elif token is Token.String.Doc: string.append(value[3:-3]) opening = None elif value == opening: opening = None if not concatenate: string = list() else: string.append(value) if opening is None: return '' return ''.join(string) def get_object(self, name): attributes = name.split('.') obj = eval(attributes.pop(0), self.interp.locals) while attributes: with inspection.AttrCleaner(obj): obj = getattr(obj, attributes.pop(0)) return obj def get_args(self): """Check if an unclosed parenthesis exists, then attempt to get the argspec() for it. On success, update self.argspec and return True, otherwise set self.argspec to None and return False""" self.current_func = None if not self.config.arg_spec: return False # Get the name of the current function and where we are in # the arguments stack = [['', 0, '']] try: for (token, value) in PythonLexer().get_tokens( self.current_line()): if token is Token.Punctuation: if value in '([{': stack.append(['', 0, value]) elif value in ')]}': stack.pop() elif value == ',': try: stack[-1][1] += 1 except TypeError: stack[-1][1] = '' stack[-1][0] = '' elif value == ':' and stack[-1][2] == 'lambda': stack.pop() else: stack[-1][0] = '' elif (token is Token.Name or token in Token.Name.subtypes or token is Token.Operator and value == '.'): stack[-1][0] += value elif token is Token.Operator and value == '=': stack[-1][1] = stack[-1][0] stack[-1][0] = '' elif token is Token.Keyword and value == 'lambda': stack.append(['', 0, value]) else: stack[-1][0] = '' while stack[-1][2] in '[{': stack.pop() _, arg_number, _ = stack.pop() func, _, _ = stack.pop() except IndexError: return False if not func: return False try: f = self.get_object(func) except (AttributeError, NameError, SyntaxError): return False if inspect.isclass(f): try: if f.__init__ is not object.__init__: f = f.__init__ except AttributeError: return None self.current_func = f self.argspec = inspection.getargspec(func, f) if self.argspec: self.argspec.append(arg_number) return True return False def get_source_of_current_name(self): """Return the source code of the object which is bound to the current name in the current input line. Return `None` if the source cannot be found.""" try: obj = self.current_func if obj is None: line = self.current_line() if inspection.is_eval_safe_name(line): obj = self.get_object(line) source = inspect.getsource(obj) except (AttributeError, IOError, NameError, TypeError): return None else: return source def complete(self, tab=False): """Construct a full list of possible completions and construct and display them in a window. Also check if there's an available argspec (via the inspect module) and bang that on top of the completions too. The return value is whether the list_win is visible or not.""" self.docstring = None if not self.get_args(): self.argspec = None elif self.current_func is not None: try: self.docstring = pydoc.getdoc(self.current_func) except IndexError: self.docstring = None else: # pydoc.getdoc() returns an empty string if no # docstring was found if not self.docstring: self.docstring = None cw = self.cw() cs = self.current_string() if not cw: self.matches = [] self.matches_iter.update() if not (cw or cs): return bool(self.argspec) if cs and tab: # Filename completion self.matches = list() username = cs.split(os.path.sep, 1)[0] user_dir = os.path.expanduser(username) for filename in glob(os.path.expanduser(cs + '*')): if os.path.isdir(filename): filename += os.path.sep if cs.startswith('~'): filename = username + filename[len(user_dir):] self.matches.append(filename) self.matches_iter.update(cs, self.matches) return bool(self.matches) elif cs: # Do not provide suggestions inside strings, as one cannot tab # them so they would be really confusing. self.matches_iter.update() return False # Check for import completion e = False matches = importcompletion.complete(self.current_line(), cw) if matches is not None and not matches: self.matches = [] self.matches_iter.update() return False if matches is None: # Nope, no import, continue with normal completion try: self.completer.complete(cw, 0) except Exception: # This sucks, but it's either that or list all the exceptions that could # possibly be raised here, so if anyone wants to do that, feel free to send me # a patch. XXX: Make sure you raise here if you're debugging the completion # stuff ! e = True else: matches = self.completer.matches if (self.config.complete_magic_methods and self.buffer and self.buffer[0].startswith("class ") and self.current_line().lstrip().startswith("def ")): matches.extend(name for name in self.config.magic_methods if name.startswith(cw)) if not e and self.argspec: matches.extend(name + '=' for name in self.argspec[1][0] if isinstance(name, basestring) and name.startswith(cw)) if py3: matches.extend(name + '=' for name in self.argspec[1][4] if name.startswith(cw)) # unless the first character is a _ filter out all attributes starting with a _ if not e and not cw.split('.')[-1].startswith('_'): matches = [match for match in matches if not match.split('.')[-1].startswith('_')] if e or not matches: self.matches = [] self.matches_iter.update() if not self.argspec: return False else: # remove duplicates self.matches = sorted(set(matches)) if len(self.matches) == 1 and not self.config.auto_display_list: self.list_win_visible = True self.tab() return False self.matches_iter.update(cw, self.matches) return True def format_docstring(self, docstring, width, height): """Take a string and try to format it into a sane list of strings to be put into the suggestion box.""" lines = docstring.split('\n') out = [] i = 0 for line in lines: i += 1 if not line.strip(): out.append('\n') for block in textwrap.wrap(line, width): out.append(' ' + block + '\n') if i >= height: return out i += 1 # Drop the last newline out[-1] = out[-1].rstrip() return out def next_indentation(self): """Return the indentation of the next line based on the current input buffer.""" if self.buffer: indentation = next_indentation(self.buffer[-1], self.config.tab_length) if indentation and self.config.dedent_after > 0: line_is_empty = lambda line: not line.strip() empty_lines = takewhile(line_is_empty, reversed(self.buffer)) if sum(1 for _ in empty_lines) >= self.config.dedent_after: indentation -= 1 else: indentation = 0 return indentation def formatforfile(self, s): """Format the stdout buffer to something suitable for writing to disk, i.e. without >>> and ... at input lines and with "# OUT: " prepended to output lines.""" def process(): for line in s.split('\n'): if line.startswith(self.ps1): yield line[len(self.ps1):] elif line.startswith(self.ps2): yield line[len(self.ps2):] elif line.rstrip(): yield "# OUT: %s" % (line,) return "\n".join(process()) def write2file(self): """Prompt for a filename and write the current contents of the stdout buffer to disk.""" try: fn = self.interact.file_prompt('Save to file (Esc to cancel): ') if not fn: self.interact.notify("Save cancelled.") return except ValueError: self.interact.notify("Save cancelled.") return if fn.startswith('~'): fn = os.path.expanduser(fn) s = self.formatforfile(self.getstdout()) try: f = open(fn, 'w') f.write(s) f.close() except IOError: self.interact.notify("Disk write error for file '%s'." % (fn, )) else: self.interact.notify('Saved to %s' % (fn, )) def pastebin(self, s=None): """Upload to a pastebin and display the URL in the status bar.""" if s is None: s = self.getstdout() if (self.config.pastebin_confirm and not self.interact.confirm(_("Pastebin buffer? (y/N) "))): self.interact.notify(_("Pastebin aborted")) return return self.do_pastebin(s) def do_pastebin(self, s): """Actually perform the upload.""" if s == self.prev_pastebin_content: self.interact.notify(_('Duplicate pastebin. Previous URL: %s') % (self.prev_pastebin_url, )) return self.prev_pastebin_url if self.config.pastebin_helper: return self.do_pastebin_helper(s) else: return self.do_pastebin_xmlrpc(s) def do_pastebin_xmlrpc(self, s): """Upload to pastebin via XML-RPC.""" try: pasteservice = ServerProxy(self.config.pastebin_url) except IOError, e: self.interact.notify(_("Pastebin error for URL '%s': %s") % (self.config.pastebin_url, str(e))) return self.interact.notify(_('Posting data to pastebin...')) try: paste_id = pasteservice.pastes.newPaste('pycon', s, '', '', '', self.config.pastebin_private) except (SocketError, XMLRPCError), e: self.interact.notify(_('Upload failed: %s') % (str(e), ) ) return self.prev_pastebin_content = s paste_url_template = Template(self.config.pastebin_show_url) paste_id = urlquote(paste_id) paste_url = paste_url_template.safe_substitute(paste_id=paste_id) self.prev_pastebin_url = paste_url self.interact.notify(_('Pastebin URL: %s') % (paste_url, ), 10) return paste_url def do_pastebin_helper(self, s): """Call out to helper program for pastebin upload.""" self.interact.notify(_('Posting data to pastebin...')) try: helper = subprocess.Popen('', executable=self.config.pastebin_helper, stdin=subprocess.PIPE, stdout=subprocess.PIPE) helper.stdin.write(s.encode(getpreferredencoding())) output = helper.communicate()[0].decode(getpreferredencoding()) paste_url = output.split()[0] except OSError, e: if e.errno == errno.ENOENT: self.interact.notify(_('Upload failed: ' 'Helper program not found.')) else: self.interact.notify(_('Upload failed: ' 'Helper program could not be run.')) return if helper.returncode != 0: self.interact.notify(_('Upload failed: ' 'Helper program returned non-zero exit ' 'status %s.' % (helper.returncode, ))) return if not paste_url: self.interact.notify(_('Upload failed: ' 'No output from helper program.')) return else: parsed_url = urlparse(paste_url) if (not parsed_url.scheme or any(unicodedata.category(c) == 'Cc' for c in paste_url)): self.interact.notify(_("Upload failed: " "Failed to recognize the helper " "program's output as an URL.")) return self.prev_pastebin_content = s self.interact.notify(_('Pastebin URL: %s') % (paste_url, ), 10) return paste_url def push(self, s, insert_into_history=True): """Push a line of code onto the buffer so it can process it all at once when a code block ends""" s = s.rstrip('\n') self.buffer.append(s) if insert_into_history: if self.config.hist_length: histfilename = os.path.expanduser(self.config.hist_file) oldhistory = self.rl_history.entries self.rl_history.entries = [] if os.path.exists(histfilename): self.rl_history.load(histfilename, getpreferredencoding()) self.rl_history.append(s) try: self.rl_history.save(histfilename, getpreferredencoding(), self.config.hist_length) except EnvironmentError, err: self.interact.notify("Error occured while writing to file %s (%s) " % (histfilename, err.strerror)) self.rl_history.entries = oldhistory self.rl_history.append(s) else: self.rl_history.append(s) more = self.interp.runsource('\n'.join(self.buffer)) if not more: self.buffer = [] return more def undo(self, n=1): """Go back in the undo history n steps and call reeavluate() Note that in the program this is called "Rewind" because I want it to be clear that this is by no means a true undo implementation, it is merely a convenience bonus.""" if not self.history: return None if len(self.history) < n: n = len(self.history) entries = list(self.rl_history.entries) self.history = self.history[:-n] self.reevaluate() self.rl_history.entries = entries def flush(self): """Olivier Grisel brought it to my attention that the logging module tries to call this method, since it makes assumptions about stdout that may not necessarily be true. The docs for sys.stdout say: "stdout and stderr needn't be built-in file objects: any object is acceptable as long as it has a write() method that takes a string argument." So I consider this to be a bug in logging, and this is a hack to fix it, unfortunately. I'm sure it's not the only module to do it.""" def close(self): """See the flush() method docstring.""" def tokenize(self, s, newline=False): """Tokenize a line of code.""" source = '\n'.join(self.buffer + [s]) cursor = len(source) - self.cpos if self.cpos: cursor += 1 stack = list() all_tokens = list(PythonLexer().get_tokens(source)) # Unfortunately, Pygments adds a trailing newline and strings with # no size, so strip them while not all_tokens[-1][1]: all_tokens.pop() all_tokens[-1] = (all_tokens[-1][0], all_tokens[-1][1].rstrip('\n')) line = pos = 0 parens = dict(zip('{([', '})]')) line_tokens = list() saved_tokens = list() search_for_paren = True for (token, value) in split_lines(all_tokens): pos += len(value) if token is Token.Text and value == '\n': line += 1 # Remove trailing newline line_tokens = list() saved_tokens = list() continue line_tokens.append((token, value)) saved_tokens.append((token, value)) if not search_for_paren: continue under_cursor = (pos == cursor) if token is Token.Punctuation: if value in parens: if under_cursor: line_tokens[-1] = (Parenthesis.UnderCursor, value) # Push marker on the stack stack.append((Parenthesis, value)) else: stack.append((line, len(line_tokens) - 1, line_tokens, value)) elif value in parens.itervalues(): saved_stack = list(stack) try: while True: opening = stack.pop() if parens[opening[-1]] == value: break except IndexError: # SyntaxError.. more closed parentheses than # opened or a wrong closing paren opening = None if not saved_stack: search_for_paren = False else: stack = saved_stack if opening and opening[0] is Parenthesis: # Marker found line_tokens[-1] = (Parenthesis, value) search_for_paren = False elif opening and under_cursor and not newline: if self.cpos: line_tokens[-1] = (Parenthesis.UnderCursor, value) else: # The cursor is at the end of line and next to # the paren, so it doesn't reverse the paren. # Therefore, we insert the Parenthesis token # here instead of the Parenthesis.UnderCursor # token. line_tokens[-1] = (Parenthesis, value) (lineno, i, tokens, opening) = opening if lineno == len(self.buffer): self.highlighted_paren = (lineno, saved_tokens) line_tokens[i] = (Parenthesis, opening) else: self.highlighted_paren = (lineno, list(tokens)) # We need to redraw a line tokens[i] = (Parenthesis, opening) self.reprint_line(lineno, tokens) search_for_paren = False elif under_cursor: search_for_paren = False if line != len(self.buffer): return list() return line_tokens def clear_current_line(self): """This is used as the exception callback for the Interpreter instance. It prevents autoindentation from occuring after a traceback.""" def next_indentation(line, tab_length): """Given a code line, return the indentation of the next line.""" line = line.expandtabs(tab_length) indentation = (len(line) - len(line.lstrip(' '))) // tab_length if line.rstrip().endswith(':'): indentation += 1 elif indentation >= 1: if line.lstrip().startswith(('return', 'pass', 'raise', 'yield')): indentation -= 1 return indentation def next_token_inside_string(s, inside_string): """Given a code string s and an initial state inside_string, return whether the next token will be inside a string or not.""" for token, value in PythonLexer().get_tokens(s): if token is Token.String: value = value.lstrip('bBrRuU') if value in ['"""', "'''", '"', "'"]: if not inside_string: inside_string = value elif value == inside_string: inside_string = False return inside_string def split_lines(tokens): for (token, value) in tokens: if not value: continue while value: head, newline, value = value.partition('\n') yield (token, head) if newline: yield (Token.Text, newline) def token_is(token_type): """Return a callable object that returns whether a token is of the given type `token_type`.""" def token_is_type(token): """Return whether a token is of a certain type or not.""" token = token[0] while token is not token_type and token.parent: token = token.parent return token is token_type return token_is_type def token_is_any_of(token_types): """Return a callable object that returns whether a token is any of the given types `token_types`.""" is_token_types = map(token_is, token_types) def token_is_any_of(token): return any(check(token) for check in is_token_types) return token_is_any_of def extract_exit_value(args): """Given the arguments passed to `SystemExit`, return the value that should be passed to `sys.exit`. """ if len(args) == 0: return None elif len(args) == 1: return args[0] else: return args bpython-0.12/bpython/_internal.py0000644000175000017500000000115212035242140017413 0ustar simonsimon00000000000000import pydoc import sys from bpython.pager import page # Ugly monkeypatching pydoc.pager = page class _Helper(object): def __init__(self): if hasattr(pydoc.Helper, "output"): # See issue #228 self.helper = pydoc.Helper(sys.stdin, None) else: self.helper = pydoc.Helper(sys.stdin, sys.stdout) def __repr__(self): return ("Type help() for interactive help, " "or help(object) for help about object.") def __call__(self, *args, **kwargs): self.helper(*args, **kwargs) _help = _Helper() # vim: sw=4 ts=4 sts=4 ai et bpython-0.12/bpython/gtk_.py0000644000175000017500000007414612061377417016420 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2011 the bpython authors. # # 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. # # This module is called "bpython.gtk_" to avoid name clashes with the # "original" gtk module. I first had used an absolute_import import from # the future to avoid that, but people are stupid and add the package path # to sys.path. from __future__ import with_statement import inspect import optparse import os import sys from locale import getpreferredencoding import gobject import gtk import pango from bpython import importcompletion, repl, translations from bpython._py3compat import PythonLexer, py3 from bpython.formatter import theme_map from bpython.translations import _ import bpython.args _COLORS = dict(b='blue', c='cyan', g='green', m='magenta', r='red', w='white', y='yellow', k='black', d='black') def add_tags_to_buffer(color_scheme, text_buffer): tags = dict() for (name, value) in color_scheme.iteritems(): tag = tags[name] = text_buffer.create_tag(name) for (char, prop) in zip(value, ['foreground', 'background']): if char.lower() == 'd': continue tag.set_property(prop, _COLORS[char.lower()]) if char.isupper(): tag.set_property('weight', pango.WEIGHT_BOLD) return tags class ArgspecFormatter(object): """ Format an argspec using Pango markup language. """ def format(self, args, varargs, varkw, defaults, in_arg): self.args_seen = 0 self.in_arg = in_arg return inspect.formatargspec(args, varargs, varkw, defaults, self.formatarg, formatvalue=self.formatvalue) def formatarg(self, name): if name == self.in_arg or self.args_seen == self.in_arg: string = '%s' % (name, ) else: string = name self.args_seen += 1 return string def formatvalue(self, value): return '=%r' % (value, ) class ExceptionDialog(gtk.MessageDialog): def __init__(self, exc_type, exc_value, tb, text=None): if text is None: text = _('An error occurred.') gtk.MessageDialog.__init__(self, buttons=gtk.BUTTONS_CLOSE, type=gtk.MESSAGE_ERROR, message_format=text) self.set_resizable(True) import cgitb text = cgitb.text((exc_type, exc_value, tb), 5) expander = gtk.Expander(_('Exception details')) self.vbox.pack_start(expander) textview = gtk.TextView() textview.get_buffer().set_text(text) scrolled_window = gtk.ScrolledWindow() scrolled_window.add(textview) expander.add(scrolled_window) self.show_all() class ExceptionManager(object): """ A context manager which runs the dialog `DialogType` on error, with the exception's type, value, a traceback and a text to display as arguments. """ def __init__(self, DialogType, text=None): self.DialogType = DialogType self.text = text or _('An error occurred.') def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if not (exc_value is None or issubclass(exc_type, (KeyboardInterrupt, SystemExit))): dialog = self.DialogType(exc_type, exc_value, traceback, self.text) dialog.run() dialog.destroy() class Nested(object): """ A helper class, inspired by a semaphore. """ def __init__(self): self.counter = 0 def __enter__(self): self.counter += 1 return self def __exit__(self, exc_type, exc_value, exc_tb): self.counter -= 1 def __nonzero__(self): return bool(self.counter) class Statusbar(gtk.Statusbar): """Contains feedback messages""" def __init__(self): gtk.Statusbar.__init__(self) self.context_id = self.get_context_id(_('Statusbar')) def message(self, s, n=3): self.clear() self.push(self.context_id, s) gobject.timeout_add(n*1000, self.clear) def clear(self): self.pop(self.context_id) # To stop the timeout from firing again return False class SuggestionWindow(gtk.Window): """ The window where suggestions are displayed. """ __gsignals__ = dict(expose_event=None, selection_changed=(gobject.SIGNAL_RUN_LAST, None, (str, ))) def __init__(self): gtk.Window.__init__(self, gtk.WINDOW_POPUP) self.set_app_paintable(True) self.set_border_width(4) self.set_decorated(False) self.set_name('gtk-tooltips') self.argspec_formatter = ArgspecFormatter() vbox = gtk.VBox(homogeneous=False) vbox.set_style(self.get_style()) self.argspec_label = gtk.Label() self.argspec_label.set_alignment(0, 0) self.argspec_label.set_line_wrap(True) self.argspec_label.set_use_markup(True) vbox.pack_start(self.argspec_label, expand=False) self.docstring_label = gtk.Label() self.docstring_label.set_alignment(0, 0) style = self.docstring_label.get_style() #color = _COLORS[self.config.color_scheme['comment'].lower()] #color = gtk.gdk.color_parse(color) #style.fg[gtk.STATE_NORMAL] = color self.docstring_label.set_style(style) vbox.pack_start(self.docstring_label, expand=False) self.model = gtk.ListStore(str, str) self.view = gtk.TreeView(self.model) self.view.set_headers_visible(False) self.view.set_style(self.get_style()) column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0, background=1) self.view.append_column(column) self.view.get_selection().connect('changed', self.on_selection_changed) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.add(self.view) vbox.pack_start(sw) self.add(vbox) self.resize(300, 150) self.show_all() def back(self): if len(self.model): self.select(-1) def do_expose_event(self, event): """ Draw a flat box around the popup window on expose event. """ width, height = self.get_size() self.style.paint_flat_box(self.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None, self, _('tooltip'), 0, 0, width, height) gtk.Window.do_expose_event(self, event) def forward(self): if len(self.model): self.select(1) def on_selection_changed(self, selection): model, iter_ = selection.get_selected() if iter_ is not None: value = model.get_value(iter_, 0) self.emit('selection-changed', value) def select(self, offset): """ Select the suggestions at offset `offset`. """ selection = self.view.get_selection() model, iter_ = selection.get_selected() if iter_ is not None: row = model.get_path(iter_)[0] row += offset else: row = 0 if row < 0: row = len(model) - 1 elif row >= len(model): row = 0 iter_ = model.get_iter(row) selection.select_iter(iter_) self.view.scroll_to_cell(row) def update_argspec(self, argspec): if argspec: func_name, args, is_bound_method, in_arg = argspec[:4] args, varargs, varkw, defaults = args[:4] if is_bound_method and isinstance(in_arg, int): in_arg += 1 argspec = self.argspec_formatter.format(args, varargs, varkw, defaults, in_arg) markup = '%s%s' % (func_name, argspec) self.argspec_label.set_markup(markup) self.argspec_label.set_property('visible', bool(argspec)) def update_docstring(self, docstring): self.docstring_label.set_text(docstring) self.docstring_label.set_property('visible', bool(docstring)) def update_matches(self, matches): self.model.clear() bg = self.get_style().bg[gtk.STATE_NORMAL] for match in matches: self.model.append([match, bg.to_string()]) self.view.set_property('visible', bool(matches)) class GTKInteraction(repl.Interaction): def __init__(self, config, statusbar): repl.Interaction.__init__(self, config, statusbar) def confirm(self, q): dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, q) response = dialog.run() dialog.destroy() return response == gtk.RESPONSE_YES def file_prompt(self, s): chooser = gtk.FileChooserDialog(title=_("File to save to"), action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) chooser.set_current_name('test.py') chooser.set_current_folder(os.path.expanduser('~')) pyfilter = gtk.FileFilter() pyfilter.set_name(_("Python files")) pyfilter.add_pattern("*.py") chooser.add_filter(pyfilter) allfilter = gtk.FileFilter() allfilter.set_name(_("All files")) allfilter.add_pattern("*") chooser.add_filter(allfilter) response = chooser.run() if response == gtk.RESPONSE_OK: fn = chooser.get_filename() else: fn = False chooser.destroy() return fn def notify(self, s, n=10): self.statusbar.message(s) class ReplWidget(gtk.TextView, repl.Repl): __gsignals__ = dict(button_press_event=None, focus_in_event=None, focus_out_event=None, realize=None, exit_event=(gobject.SIGNAL_RUN_LAST, None, ())) def __init__(self, interpreter, config): gtk.TextView.__init__(self) repl.Repl.__init__(self, interpreter, config) self.interp.writetb = self.writetb self.exit_value = () self.editing = Nested() self.reset_indent = False self.modify_font(pango.FontDescription(self.config.gtk_font)) self.set_wrap_mode(gtk.WRAP_CHAR) self.list_win = SuggestionWindow() self.list_win.connect('selection-changed', self.on_suggestion_selection_changed) self.list_win.hide() self.modify_base('normal', gtk.gdk.color_parse(_COLORS[self.config.color_gtk_scheme['background']])) self.text_buffer = self.get_buffer() self.interact = GTKInteraction(self.config, Statusbar()) tags = add_tags_to_buffer(self.config.color_gtk_scheme, self.text_buffer) tags['prompt'].set_property('editable', False) self.text_buffer.connect('delete-range', self.on_buf_delete_range) self.text_buffer.connect('insert-text', self.on_buf_insert_text) self.text_buffer.connect('mark-set', self.on_buf_mark_set) def change_line(self, line): """ Replace the current input line with `line`. """ with self.editing: self.text_buffer.delete(self.get_line_start_iter(), self.get_line_end_iter()) if self.config.syntax: self.insert_highlighted(self.get_line_start_iter(), line) else: self.text_buffer.insert(self.get_line_start_iter(), line) def clear_current_line(self): """ Called when a SyntaxError occurs. """ repl.Repl.clear_current_line(self) self.reset_indent = True def complete(self): if self.config.auto_display_list: self.list_win_visible = repl.Repl.complete(self) if self.list_win_visible: self.list_win.update_argspec(self.argspec) self.list_win.update_docstring(self.docstring or '') self.list_win.update_matches(self.matches) iter_rect = self.get_iter_location(self.get_cursor_iter()) x, y = self.window.get_origin() _, height = self.get_line_yrange(self.get_cursor_iter()) self.list_win.move(x + iter_rect.x, y + iter_rect.y + height) self.list_win.show() else: self.list_win.hide() @property def cpos(self): cpos = (self.get_line_end_iter().get_offset() - self.get_cursor_iter().get_offset()) if cpos and not self.get_overwrite(): cpos += 1 return cpos def cw(self): """ Return the current word. """ return self.text_buffer.get_text(self.get_word_start_iter(), self.get_cursor_iter()) def current_line(self): """ Return the current input line. """ return self.text_buffer.get_slice(self.get_line_start_iter(), self.get_line_end_iter()) def echo(self, string): with self.editing: self.text_buffer.insert_with_tags_by_name(self.get_cursor_iter(), string, 'output') self.move_cursor(len(string)) def get_cursor_iter(self): """ Return an iter where the cursor currently is. """ cursor_marker = self.text_buffer.get_insert() return self.text_buffer.get_iter_at_mark(cursor_marker) def get_line_start_iter(self): """ Return an iter where the current line starts. """ line_start_marker = self.text_buffer.get_mark('line_start') if line_start_marker is None: return self.text_buffer.get_start_iter() return self.text_buffer.get_iter_at_mark(line_start_marker) def get_line_end_iter(self): """ Return an iter where the current line ends. """ iter_ = self.get_line_start_iter() if not iter_.ends_line() and not iter_.forward_to_line_end(): iter_ = self.text_buffer.get_end_iter() return iter_ def get_word_start_iter(self): iter_ = self.get_cursor_iter() pred = lambda char, _: not (char.isalnum() or char in '_.') if iter_.backward_find_char(pred, None, self.get_line_start_iter()): iter_.forward_char() return iter_ def do_button_press_event(self, event): if self.list_win_visible: self.list_win.hide() return gtk.TextView.do_button_press_event(self, event) def do_focus_in_event(self, event): if self.list_win_visible: self.list_win.show() return gtk.TextView.do_focus_in_event(self, event) def do_focus_out_event(self, event): if self.list_win_visible: self.list_win.hide() return gtk.TextView.do_focus_out_event(self, event) def do_key_press_event(self, event): state = event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK | gtk.gdk.MOD4_MASK | gtk.gdk.SHIFT_MASK) if not state: if event.keyval == gtk.keysyms.F2: source = self.get_source_of_current_name() if source is not None: show_source_in_new_window(source, self.config.color_gtk_scheme, self.config.syntax) else: self.interact.notify(_('Cannot show source.')) elif event.keyval == gtk.keysyms.Return: if self.list_win_visible: self.list_win_visible = False self.list_win.hide() self.rl_history.reset() line = self.current_line() more = self.push_line() self.prompt(more) if self.reset_indent: self.reset_indent = False else: indentation = self.next_indentation() if indentation: with self.editing: self.text_buffer.insert(self.get_cursor_iter(), '\t' * indentation) self.move_cursor(indentation) return True elif event.keyval == gtk.keysyms.Tab and self.list_win_visible: self.list_win.forward() return True elif event.keyval == gtk.keysyms.Up: if self.list_win_visible: self.list_win.back() else: if not self.rl_history.is_at_end: self.rl_history.enter(self.current_line()) self.change_line(self.rl_history.back()) self.text_buffer.place_cursor(self.get_line_end_iter()) return True elif event.keyval == gtk.keysyms.Down: if self.list_win_visible: self.list_win.forward() else: if not self.rl_history.is_at_start: self.rl_history.enter(self.current_line()) self.change_line(self.rl_history.forward()) self.text_buffer.place_cursor(self.get_line_end_iter()) return True elif state & gtk.gdk.SHIFT_MASK: if (event.keyval == gtk.keysyms.ISO_Left_Tab and self.list_win_visible): self.list_win.back() return True elif state & gtk.gdk.CONTROL_MASK: if event.string == chr(4) and not self.current_line(): self.emit('exit-event') return True return gtk.TextView.do_key_press_event(self, event) def do_realize(self): gtk.TextView.do_realize(self) self.prompt(False) def highlight(self, start_iter, tokens): """ Highlight the text starting at `start_iter` using `tokens`. """ token_start_iter = start_iter.copy() token_end_iter = start_iter.copy() for (token, value) in tokens: while token not in theme_map: token = token.parent token_end_iter.forward_chars(len(value)) self.text_buffer.apply_tag_by_name(theme_map[token], token_start_iter, token_end_iter) token_start_iter.forward_chars(len(value)) def highlight_current_line(self): """ Highlight the current line. """ if self.config.syntax: if self.highlighted_paren is not None: self.reprint_line(*self.highlighted_paren) self.highlighted_paren = None start = self.get_line_start_iter() self.text_buffer.remove_all_tags(start, self.get_line_end_iter()) self.highlight(start, self.tokenize(self.current_line())) def insert_highlighted(self, iter_, string): offset = iter_.get_offset() newline = iter_.forward_to_line_end() # self.tokenize() may call self.reprint_line(), which will # invalidate iters. tokens = self.tokenize(string, newline) iter_ = self.text_buffer.get_iter_at_offset(offset) self.insert_highlighted_tokens(iter_, tokens) def insert_highlighted_tokens(self, iter_, tokens): offset = iter_.get_offset() buffer = self.text_buffer for (token, value) in tokens: while token not in theme_map: token = token.parent iter_ = buffer.get_iter_at_offset(offset) with self.editing: buffer.insert_with_tags_by_name(iter_, value, theme_map[token]) offset += len(value) def move_cursor(self, offset): """ Move the cursor to a given offset. """ iter_ = self.get_cursor_iter() iter_.forward_chars(offset) self.text_buffer.place_cursor(iter_) return iter_ def on_buf_delete_range(self, buffer, start, end): if self.editing: return buffer.emit_stop_by_name('delete-range') # Only allow editing of the current line and not of previous ones line_start = self.get_line_start_iter() if end.compare(line_start) < 0: return elif start.compare(line_start) < 0: start = line_start with self.editing: buffer.delete(start, end) self.highlight_current_line() self.complete() def on_buf_insert_text(self, buffer, iter_, text, length): if self.editing: return self.set_cursor_to_valid_insert_position() buffer.emit_stop_by_name('insert-text') for (i, line) in enumerate(text.splitlines()): if i: self.prompt(self.push_line()) with self.editing: buffer.insert_at_cursor(line) self.highlight_current_line() self.complete() def on_buf_mark_set(self, buffer, iter_, textmark): if (textmark.get_name() == 'insert' and self.get_line_start_iter().compare(iter_) < 0): self.highlight_current_line() def on_suggestion_selection_changed(self, selection, word): with self.editing: self.text_buffer.delete(self.get_word_start_iter(), self.get_cursor_iter()) self.text_buffer.insert_at_cursor(word) def do_paste(self, widget): clipboard = gtk.clipboard_get() paste_url = self.pastebin() if paste_url: clipboard.set_text(paste_url) clipboard.store() def do_write2file(self, widget): self.write2file() def do_partial_paste(self, widget): bounds = self.text_buffer.get_selection_bounds() if bounds == (): # FIXME show a nice status bar message pass else: self.pastebin(self.text_buffer.get_text(bounds[0], bounds[1])) def write(self, s): """For overriding stdout defaults""" if '\x04' in s: for block in s.split('\x04'): self.write(block) return if s.rstrip() and '\x03' in s: t = s.split('\x03')[1] else: t = s if not py3 and isinstance(t, unicode): t = t.encode(getpreferredencoding()) self.echo(s) self.s_hist.append(s.rstrip()) def prompt(self, more): """ Show the appropriate Python prompt. """ if more: text = self.ps2 else: text = self.ps1 with self.editing: iter_ = self.get_cursor_iter() self.text_buffer.insert_with_tags_by_name(iter_, text, 'prompt') iter_.forward_chars(len(text)) mark = self.text_buffer.create_mark('line_start', iter_, True) self.text_buffer.place_cursor(iter_) self.scroll_to_mark(mark, 0.2) def push_line(self): line = self.current_line() # Save mark for easy referencing later self.text_buffer.create_mark('line%i_start' % (len(self.buffer), ), self.get_line_start_iter(), True) iter_ = self.get_line_end_iter() self.text_buffer.place_cursor(iter_) with self.editing: self.text_buffer.insert(iter_, '\n') self.move_cursor(1) self.highlight_current_line() try: return self.push(line + '\n') except SystemExit, e: self.exit_value = e.args self.emit('exit-event') return False def reprint_line(self, lineno, tokens): """ Helper function for paren highlighting: Reprint line at offset `lineno` in current input buffer. """ if not self.buffer or lineno == len(self.buffer): return mark = self.text_buffer.get_mark('line%i_start' % (lineno, )) start = self.text_buffer.get_iter_at_mark(mark) end = start.copy() end.forward_to_line_end() self.text_buffer.remove_all_tags(start, end) self.highlight(start, tokens) def set_cursor_to_valid_insert_position(self): cursor_iter = self.get_cursor_iter() line_start_iter = self.get_line_start_iter() if line_start_iter.compare(cursor_iter) > 0: self.text_buffer.place_cursor(line_start_iter) def getstdout(self): bounds = self.text_buffer.get_bounds() text = self.text_buffer.get_text(bounds[0], bounds[1]) return text def writetb(self, lines): with ExceptionManager(ExceptionDialog, 'An error occured while trying to display ' 'an error. Please contact the bpython ' 'developers.'): string = ''.join(lines) with self.editing: self.text_buffer.insert_with_tags_by_name( self.get_cursor_iter(), string, 'error' ) self.move_cursor(len(string)) def show_source_in_new_window(source, color_scheme=None, highlight=True): win = gtk.Window() sw = gtk.ScrolledWindow() view = gtk.TextView() buffer = view.get_buffer() if highlight: add_tags_to_buffer(color_scheme, buffer) for (token, value) in PythonLexer().get_tokens(source): while token not in theme_map: token = token.parent iter_ = buffer.get_end_iter() buffer.insert_with_tags_by_name(iter_, value, theme_map[token]) else: buffer.insert(buffer.get_end_iter(), source) sw.add(view) win.add(sw) win.show_all() def init_import_completion(): try: importcompletion.find_iterator.next() except StopIteration: return False else: return True def main(args=None): translations.init() gtk_options = (_('gtk-specific options'), _("Options specific to bpython's Gtk+ front end"), [optparse.Option('--socket-id', dest='socket_id', type='int', help=_('Embed bpython'))]) config, options, exec_args = bpython.args.parse(args, gtk_options, True) interpreter = repl.Interpreter(None, getpreferredencoding()) repl_widget = ReplWidget(interpreter, config) repl_widget.connect('exit-event', gtk.main_quit) gobject.idle_add(init_import_completion) if not exec_args: sys.path.insert(0, '') gobject.idle_add(repl_widget.startup) else: if options.interactive: gobject.idle_add(bpython.args.exec_code, interpreter, exec_args) else: bpython.args.exec_code(interpreter, exec_args) return 0 sys.stderr = repl_widget sys.stdout = repl_widget if not options.socket_id: parent = gtk.Window() parent.connect('delete-event', lambda widget, event: gtk.main_quit()) # branding # fix icon to be distributed and loaded from the correct path icon = gtk.gdk.pixbuf_new_from_file(os.path.join(os.path.dirname(__file__), 'logo.png')) parent.set_title('bpython') parent.set_icon(icon) parent.resize(600, 300) else: parent = gtk.Plug(options.socket_id) parent.connect('destroy', gtk.main_quit) container = gtk.VBox() parent.add(container) mb = gtk.MenuBar() filemenu = gtk.Menu() filem = gtk.MenuItem("File") filem.set_submenu(filemenu) save = gtk.ImageMenuItem(gtk.STOCK_SAVE) save.connect("activate", repl_widget.do_write2file) filemenu.append(save) pastebin = gtk.MenuItem("Pastebin") pastebin.connect("activate", repl_widget.do_paste) filemenu.append(pastebin) pastebin_partial = gtk.MenuItem(_("Pastebin selection")) pastebin_partial.connect("activate", repl_widget.do_partial_paste) filemenu.append(pastebin_partial) exit = gtk.ImageMenuItem(gtk.STOCK_QUIT) exit.connect("activate", gtk.main_quit) filemenu.append(exit) mb.append(filem) vbox = gtk.VBox(False, 2) vbox.pack_start(mb, False, False, 0) container.pack_start(vbox, expand=False) # read from config sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.add(repl_widget) container.add(sw) sb = repl_widget.interact.statusbar container.pack_end(sb, expand=False) parent.set_focus(repl_widget) parent.show_all() parent.connect('delete-event', lambda widget, event: gtk.main_quit()) try: gtk.main() except KeyboardInterrupt: pass return repl.extract_exit_value(repl_widget.exit_value) if __name__ == '__main__': from bpython.gtk_ import main sys.exit(main()) bpython-0.12/bpython/logo.png0000644000175000017500000001707312031025543016547 0ustar simonsimon00000000000000PNG  IHDRVVUVatEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp ݘCIDATx] tyKZ=,!KmLlvb0 ) P򀤜@OB$Mm$gO6 iMB$%p81`dY$}132b}|3Pc''h:tCԱ(-G]'z -‡>kDuZ$V#Qb F#D*†\-=52ӣV6S21ZobRǞȏ SW -\uNom\XӖy6zMԃ& 0lj9"p<{(e6=q_K8EH3~ww}m,}/M/|)ٸVj| 督[,qrPqb㽝O lE.{K:]7_m.fy*/;d '<ǥ5> Bab$fϠx}˪=_ 'x\,VN\ ϛi+75hb3 %?.QZ)09~89^߾>q]4iԶ岫G2zZw5(5D,by\c`r`88_բqԽ}S섗^z„;Dw 0FdiU SXd{i#rsg>Wsi A)+P |{o :(d9 ]${%(h2P"&$_Oq l}Fu͍FVW Uԍ0R_b(`9Hֶ(~j*i.]M5 $"5C/V_OƸ+Ō***,v_~D:yXx&܉S,՗\c[F: j:{5i8E"V\Q|WX\AEù̽ )eE$$2嬁pSF2˒Qq,uuWڕmk480L8ҩx:%^QXƞuSSʂsKSI .XSu+:vpcY2#Xy7t>0+`F \.u3&/uÊ**Aٗ*Aa*\mDM'19AH"zT ^T#T} Nn0c@[ f>Gc8aUb 4HK/S9 Zn s~`pLxAcB t%p6"ap?|F,!w Q`pi=FPn{_c+J+SX*UBY7 Yº;K?H$UDž| C [As 7C2]vL \%0S~n;]g6:6G6H(h|M{jMK}IJqiҩ]|S<6S8E gJ@fR7Y]x57'|Ow&r22U>0p#cã˩X E!3cJzn*vDW1,ꆍvlm8{5.0B-6_zuKCs|hcNW:񥘺k=yݖm(gU2yjFa)tJ2"$L~cD9GxJ"1 s]|ݹ˿3 o9+vefqbټ!2>'Ȅk?^i| ez%hItb{iSU٬.hȄL\ jC>Si~̎ullDkNUu0yI3BeyJC\FDU^HYXY,qbĉQB55zw|S~7)6gnB5/ Y_k۷ljGUGfly̬tx:W`eJ{)#hPRr&Z]pםnGv"a\Uu h yFVSL6a99-Eg*|es-#IǞ>?Ћ'ڗqFtB[VĒ(!r>AZٖlU 6VX%ݢޒYiquv=IF5ݫ?Һ-KT';wdcע77n +-)Om}WCt@F'%2&Ff 0el VR@vΞL/i四`Y2i`~HAD vE2{Ձ\]_k"([~/۶n$acq As+"q#*7ֶFbx/T"@xyŖ79v&غa{wskO\0XT3)Cl`)Qx\&^^.pȴ`WlZwǿظa_PX| ,4XISP>r c!XXVLrG<#;GNwI5cj9Ñs dgfK,k5Dn[*!QprDlg9B?T$j:i/HԈFB +Љ38FUd^iD(li(7;@m+v2͗{rYÚ2V(Eb-\;}|]MxJM#yfpG(}8 TZZ?tRcA="x,;(-ɆL0Xp׋`#ʯVb%DWNՊ‚jÚ ,}rﱼW(_?s,%qLuwcjg j#KXjaT=?cfƺ\KY]у`BuF _O#T9.Aa;ؿ%2 yYGV}ތk^l,5HO}U&ΚPaT{TЧSe1=t,vfI|)j?u:mV&QD41.P.{_ygy>OaTbPZE ~=)xeff*6SNJwV&l)K@lSu3ajS!W #C؊klGҖm z^=vГRU4q55M(dICtv+Ljȳ U2b(X%_M#y{ei~VZvs##cvߛ9e&RՌsL-զ?[*E^-qdTu3μĕ ڒee,A(k#Ϸ#O<+nY`8X2ISܱߗk[n\3lpSs,^[`)mѴW&%ž@PYٞ7NUY#pW:׳[dQ WN-Pt?:ѿ*UMs5҂me$GC; iTmQY,LL9;q*P)<^CG)~*2xĐcnssb.n`-!ٹ<*!ţ/nk³Z>Ū q:9t1+*v}{/ޅR0LΫ*PDkۯ.KZw~rW{ݷx͚ 訙Hۣ;5""kS?غj4.PjBv.̃#V=f`t_'p d?whD}':GS3a%z6O8oVG{tuwc*ei]m&O{GWO?0ܰb [rr*,՜dLv:Cc8>!xq_i .}򚎏\qõ+w[25%t|ٔt4XeO?NnːԭGsrgXwjKmZ_eYKr;t]+FS5zmMBKh1˓˼ *5WȧB:kGt=@7]i_됽ـumL:nZo$$jD@k$⺱jY6\qͫ)1ӶivX4inxGs:s>.Fo"js=Or>[Y۷|wFS@| W:6IENDB`bpython-0.12/bpython/pager.py0000644000175000017500000000457412031025543016553 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2011 Andreas Stuehrk # # 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. import curses import errno import os import pydoc import subprocess import sys def get_pager_command(): command = os.environ.get('PAGER', 'less -r').split() return command def page_internal(data): """A more than dumb pager function.""" if hasattr(pydoc, 'ttypager'): pydoc.ttypager(data) else: sys.stdout.write(data) def page(data, use_internal=False): command = get_pager_command() if not command or use_internal: page_internal(data) else: curses.endwin() try: popen = subprocess.Popen(command, stdin=subprocess.PIPE) if isinstance(data, unicode): data = data.encode(sys.__stdout__.encoding, 'replace') popen.stdin.write(data) popen.stdin.close() except OSError, e: if e.errno == errno.ENOENT: # pager command not found, fall back to internal pager page_internal(data) return except IOError, e: if e.errno != errno.EPIPE: raise while True: try: popen.wait() except OSError, e: if e.errno != errno.EINTR: raise else: break curses.doupdate() bpython-0.12/bpython/keys.py0000644000175000017500000000464412031025543016426 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Simon de Vlieger # # 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. # import string class KeyMap: def __init__(self): self.map = {} def __getitem__(self, key): if not key: # Unbound key return str() elif key in self.map: return self.map[key] else: raise KeyError('Configured keymap (%s)' % key + ' does not exist in bpython.keys') def __delitem__(self, key): del self.map[key] def __setitem__(self, key, value): self.map[key] = value cli_key_dispatch = KeyMap() urwid_key_dispatch = KeyMap() # fill dispatch with letters for c in string.ascii_lowercase: cli_key_dispatch['C-%s' % c] = (chr(string.ascii_lowercase.index(c) + 1), '^%s' % c.upper()) for c in string.ascii_lowercase: urwid_key_dispatch['C-%s' % c] = 'ctrl %s' % c urwid_key_dispatch['M-%s' % c] = 'meta %s' % c # fill dispatch with cool characters cli_key_dispatch['C-['] = (chr(27), '^[') cli_key_dispatch['C-\\'] = (chr(28), '^\\') cli_key_dispatch['C-]'] = (chr(29), '^]') cli_key_dispatch['C-^'] = (chr(30), '^^') cli_key_dispatch['C-_'] = (chr(31), '^_') # fill dispatch with function keys for x in xrange(1, 13): cli_key_dispatch['F%s' % str(x)] = ('KEY_F(%s)' % str(x),) for x in xrange(1, 13): urwid_key_dispatch['F%s' % str(x)] = 'f%s' % str(x) bpython-0.12/bpython/importcompletion.py0000644000175000017500000001433212035242140021050 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2011 Andreas Stuehrk # # 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. from __future__ import with_statement import imp import os import sys import warnings try: from warnings import catch_warnings except ImportError: import contextlib @contextlib.contextmanager def catch_warnings(): """Stripped-down version of `warnings.catch_warnings()` (available in Py >= 2.6).""" filters = warnings.filters warnings.filters = list(filters) try: yield finally: warnings.filters = filters from bpython._py3compat import py3 # The cached list of all known modules modules = set() fully_loaded = False def complete(line, cw): """Construct a full list of possibly completions for imports.""" if not cw: return None tokens = line.split() if tokens[0] not in ['from', 'import']: return None completing_from = False if tokens[0] == 'from': if len(tokens) > 3: if '.' in cw: # This will result in a SyntaxError, so do not return # any matches return None completing_from = True cw = '%s.%s' % (tokens[1], cw) elif len(tokens) == 3: if 'import '.startswith(cw): return ['import '] else: # Will result in a SyntaxError return None matches = list() for name in modules: if not (name.startswith(cw) and name.find('.', len(cw)) == -1): continue if completing_from: name = name[len(tokens[1]) + 1:] matches.append(name) if completing_from and tokens[1] in sys.modules: # from x import y -> search for attributes starting with y if # x is in sys.modules _, _, cw = cw.rpartition('.') module = sys.modules[tokens[1]] matches.extend(name for name in dir(module) if name.startswith(cw)) elif len(tokens) == 2: # from x.y or import x.y -> search for attributes starting # with y if x is in sys.modules and the attribute is also in # sys.modules module_name, _, cw = cw.rpartition('.') if module_name in sys.modules: module = sys.modules[module_name] for name in dir(module): if not name.startswith(cw): continue submodule_name = '%s.%s' % (module_name, name) if submodule_name in sys.modules: matches.append(submodule_name) if not matches: return [] return matches def find_modules(path): """Find all modules (and packages) for a given directory.""" if not os.path.isdir(path): # Perhaps a zip file return try: filenames = os.listdir(path) except EnvironmentError: filenames = [] for name in filenames: if not any(name.endswith(suffix[0]) for suffix in imp.get_suffixes()): # Possibly a package if '.' in name: continue elif os.path.isdir(os.path.join(path, name)): # Unfortunately, CPython just crashes if there is a directory # which ends with a python extension, so work around. continue for suffix in imp.get_suffixes(): if name.endswith(suffix[0]): name = name[:-len(suffix[0])] break if py3 and name == "badsyntax_pep3120": # Workaround for issue #166 continue try: with catch_warnings(): warnings.simplefilter("ignore", ImportWarning) fo, pathname, _ = imp.find_module(name, [path]) except (ImportError, SyntaxError): continue except UnicodeEncodeError: # Happens with Python 3 when there is a filename in some # invalid encoding continue else: if fo is not None: fo.close() else: # Yay, package for subname in find_modules(pathname): if subname != '__init__': yield '%s.%s' % (name, subname) yield name def find_all_modules(path=None): """Return a list with all modules in `path`, which should be a list of directory names. If path is not given, sys.path will be used.""" if path is None: modules.update(sys.builtin_module_names) path = sys.path for p in path: if not p: p = os.curdir for module in find_modules(p): if not py3 and not isinstance(module, unicode): try: module = module.decode(sys.getfilesystemencoding()) except UnicodeDecodeError: # Not importable anyway, ignore it continue modules.add(module) yield def find_coroutine(): global fully_loaded if fully_loaded: return None try: find_iterator.next() except StopIteration: fully_loaded = True return True def reload(): """Refresh the list of known modules.""" modules.clear() for _ in find_all_modules(): pass find_iterator = find_all_modules() bpython-0.12/bpython/autocomplete.py0000644000175000017500000001141112035242140020140 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2009-2012 the bpython authors. # # 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. # import __builtin__ import rlcompleter import re from bpython import inspection # Needed for special handling of __abstractmethods__ # abc only exists since 2.6, so check both that it exists and that it's # the one we're expecting try: import abc abc.ABCMeta has_abc = True except (ImportError, AttributeError): has_abc = False # Autocomplete modes SIMPLE = 'simple' SUBSTRING = 'substring' FUZZY = 'fuzzy' class Autocomplete(rlcompleter.Completer): """ """ def __init__(self, namespace = None, config = None): rlcompleter.Completer.__init__(self, namespace) self.locals = namespace if hasattr(config, 'autocomplete_mode'): self.autocomplete_mode = config.autocomplete_mode else: self.autocomplete_mode = SUBSTRING def attr_matches(self, text): """Taken from rlcompleter.py and bent to my will. """ # Gna, Py 2.6's rlcompleter searches for __call__ inside the # instance instead of the type, so we monkeypatch to prevent # side-effects (__getattr__/__getattribute__) m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) if not m: return [] expr, attr = m.group(1, 3) if expr.isdigit(): # Special case: float literal, using attrs here will result in # a SyntaxError return [] obj = eval(expr, self.locals) with inspection.AttrCleaner(obj): matches = self.attr_lookup(obj, expr, attr) return matches def attr_lookup(self, obj, expr, attr): """Second half of original attr_matches method factored out so it can be wrapped in a safe try/finally block in case anything bad happens to restore the original __getattribute__ method.""" words = dir(obj) if hasattr(obj, '__class__'): words.append('__class__') words = words + rlcompleter.get_class_members(obj.__class__) if has_abc and not isinstance(obj.__class__, abc.ABCMeta): try: words.remove('__abstractmethods__') except ValueError: pass matches = [] n = len(attr) for word in words: if self.method_match(word, n, attr) and word != "__builtins__": matches.append("%s.%s" % (expr, word)) return matches def _callable_postfix(self, value, word): """rlcompleter's _callable_postfix done right.""" with inspection.AttrCleaner(value): if inspection.is_callable(value): word += '(' return word def global_matches(self, text): """Compute matches when text is a simple name. Return a list of all keywords, built-in functions and names currently defined in self.namespace that match. """ hash = {} n = len(text) import keyword for word in keyword.kwlist: if self.method_match(word, n, text): hash[word] = 1 for nspace in [__builtin__.__dict__, self.namespace]: for word, val in nspace.items(): if self.method_match(word, len(text), text) and word != "__builtins__": hash[self._callable_postfix(val, word)] = 1 matches = hash.keys() matches.sort() return matches def method_match(self, word, size, text): if self.autocomplete_mode == SIMPLE: return word[:size] == text elif self.autocomplete_mode == SUBSTRING: s = r'.*%s.*' % text return re.search(s, word) else: s = r'.*%s.*' % '.*'.join(list(text)) return re.search(s, word) bpython-0.12/bpython/cli.py0000644000175000017500000017031612061377417016237 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Bob Farrell # Copyright (c) bpython authors # # 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. # # Modified by Brandon Navra # Notes for Windows # Prerequsites # - Curses # - pyreadline # # Added # # - Support for running on windows command prompt # - input from numpad keys # # Issues # # - Suspend doesn't work nor does detection of resizing of screen # - Instead the suspend key exits the program # - View source doesn't work on windows unless you install the less program (From GnuUtils or Cygwin) from __future__ import division, with_statement import platform import os import sys import curses import math import re import time import struct if platform.system() != 'Windows': import signal #Windows does not have job control import termios #Windows uses curses import fcntl #Windows uses curses import unicodedata import errno import locale from types import ModuleType # These are used for syntax highlighting from pygments import format from pygments.formatters import TerminalFormatter from pygments.lexers import PythonLexer from pygments.token import Token from bpython.formatter import BPythonFormatter # This for completion from bpython import importcompletion # This for config from bpython.config import Struct # This for keys from bpython.keys import cli_key_dispatch as key_dispatch # This for i18n from bpython import translations from bpython.translations import _ from bpython import repl from bpython._py3compat import py3 from bpython.pager import page from bpython import autocomplete import bpython.args if not py3: import inspect # --- module globals --- stdscr = None colors = None DO_RESIZE = False # --- def getpreferredencoding(): return locale.getpreferredencoding() or sys.getdefaultencoding() def calculate_screen_lines(tokens, width, cursor=0): """Given a stream of tokens and a screen width plus an optional initial cursor position, return the amount of needed lines on the screen.""" lines = 1 pos = cursor for (token, value) in tokens: if token is Token.Text and value == '\n': lines += 1 else: pos += len(value) lines += pos // width pos %= width return lines class FakeStream(object): """Provide a fake file object which calls functions on the interface provided.""" def __init__(self, interface): self.encoding = getpreferredencoding() self.interface = interface def write(self, s): self.interface.write(s) def writelines(self, l): for s in l: self.write(s) def isatty(self): # some third party (amongst them mercurial) depend on this return True class FakeStdin(object): """Provide a fake stdin type for things like raw_input() etc.""" def __init__(self, interface): """Take the curses Repl on init and assume it provides a get_key method which, fortunately, it does.""" self.encoding = getpreferredencoding() self.interface = interface self.buffer = list() def __iter__(self): return iter(self.readlines()) def flush(self): """Flush the internal buffer. This is a no-op. Flushing stdin doesn't make any sense anyway.""" def write(self, value): # XXX IPython expects sys.stdin.write to exist, there will no doubt be # others, so here's a hack to keep them happy raise IOError(errno.EBADF, "sys.stdin is read-only") def isatty(self): return True def readline(self, size=-1): """I can't think of any reason why anything other than readline would be useful in the context of an interactive interpreter so this is the only one I've done anything with. The others are just there in case someone does something weird to stop it from blowing up.""" if not size: return '' elif self.buffer: buffer = self.buffer.pop(0) else: buffer = '' curses.raw(True) try: while not buffer.endswith(('\n', '\r')): key = self.interface.get_key() if key in [curses.erasechar(), 'KEY_BACKSPACE']: y, x = self.interface.scr.getyx() if buffer: self.interface.scr.delch(y, x - 1) buffer = buffer[:-1] continue elif key == chr(4) and not buffer: # C-d return '' elif (key not in ('\n', '\r') and (len(key) > 1 or unicodedata.category(key) == 'Cc')): continue sys.stdout.write(key) # Include the \n in the buffer - raw_input() seems to deal with trailing # linebreaks and will break if it gets an empty string. buffer += key finally: curses.raw(False) if size > 0: rest = buffer[size:] if rest: self.buffer.append(rest) buffer = buffer[:size] if py3: return buffer else: return buffer.encode(getpreferredencoding()) def read(self, size=None): if size == 0: return '' data = list() while size is None or size > 0: line = self.readline(size or -1) if not line: break if size is not None: size -= len(line) data.append(line) return ''.join(data) def readlines(self, size=-1): return list(iter(self.readline, '')) # TODO: # # Tab completion does not work if not at the end of the line. # # Numerous optimisations can be made but it seems to do all the lookup stuff # fast enough on even my crappy server so I'm not too bothered about that # at the moment. # # The popup window that displays the argspecs and completion suggestions # needs to be an instance of a ListWin class or something so I can wrap # the addstr stuff to a higher level. # def get_color(config, name): global colors return colors[config.color_scheme[name].lower()] def get_colpair(config, name): return curses.color_pair(get_color(config, name) + 1) def make_colors(config): """Init all the colours in curses and bang them into a dictionary""" # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default: c = { 'k': 0, 'r': 1, 'g': 2, 'y': 3, 'b': 4, 'm': 5, 'c': 6, 'w': 7, 'd': -1, } if platform.system() == 'Windows': c = dict(c.items() + [ ('K', 8), ('R', 9), ('G', 10), ('Y', 11), ('B', 12), ('M', 13), ('C', 14), ('W', 15), ] ) for i in range(63): if i > 7: j = i // 8 else: j = c[config.color_scheme['background']] curses.init_pair(i + 1, i % 8, j) return c class CLIInteraction(repl.Interaction): def __init__(self, config, statusbar=None): repl.Interaction.__init__(self, config, statusbar) def confirm(self, q): """Ask for yes or no and return boolean""" try: reply = self.statusbar.prompt(q) except ValueError: return False return reply.lower() in (_('y'), _('yes')) def notify(self, s, n=10): return self.statusbar.message(s, n) def file_prompt(self, s): return self.statusbar.prompt(s) class CLIRepl(repl.Repl): def __init__(self, scr, interp, statusbar, config, idle=None): repl.Repl.__init__(self, interp, config) self.interp.writetb = self.writetb self.scr = scr self.stdout_hist = '' self.list_win = newwin(get_colpair(config, 'background'), 1, 1, 1, 1) self.cpos = 0 self.do_exit = False self.exit_value = () self.f_string = '' self.idle = idle self.in_hist = False self.paste_mode = False self.last_key_press = time.time() self.s = '' self.statusbar = statusbar self.formatter = BPythonFormatter(config.color_scheme) self.interact = CLIInteraction(self.config, statusbar=self.statusbar) if config.cli_suggestion_width <= 0 or config.cli_suggestion_width > 1: config.cli_suggestion_width = 0.8 def addstr(self, s): """Add a string to the current input line and figure out where it should go, depending on the cursor position.""" self.rl_history.reset() if not self.cpos: self.s += s else: l = len(self.s) self.s = self.s[:l - self.cpos] + s + self.s[l - self.cpos:] self.complete() def atbol(self): """Return True or False accordingly if the cursor is at the beginning of the line (whitespace is ignored). This exists so that p_key() knows how to handle the tab key being pressed - if there is nothing but white space before the cursor then process it as a normal tab otherwise attempt tab completion.""" return not self.s.lstrip() def bs(self, delete_tabs=True): """Process a backspace""" self.rl_history.reset() y, x = self.scr.getyx() if not self.s: return if x == self.ix and y == self.iy: return n = 1 self.clear_wrapped_lines() if not self.cpos: # I know the nested if blocks look nasty. :( if self.atbol() and delete_tabs: n = len(self.s) % self.config.tab_length if not n: n = self.config.tab_length self.s = self.s[:-n] else: self.s = self.s[:-self.cpos - 1] + self.s[-self.cpos:] self.print_line(self.s, clr=True) return n def bs_word(self): self.rl_history.reset() pos = len(self.s) - self.cpos - 1 deleted = [] # First we delete any space to the left of the cursor. while pos >= 0 and self.s[pos] == ' ': deleted.append(self.s[pos]) pos -= self.bs() # Then we delete a full word. while pos >= 0 and self.s[pos] != ' ': deleted.append(self.s[pos]) pos -= self.bs() return ''.join(reversed(deleted)) def check(self): """Check if paste mode should still be active and, if not, deactivate it and force syntax highlighting.""" if (self.paste_mode and time.time() - self.last_key_press > self.config.paste_time): self.paste_mode = False self.print_line(self.s) def clear_current_line(self): """Called when a SyntaxError occured in the interpreter. It is used to prevent autoindentation from occuring after a traceback.""" repl.Repl.clear_current_line(self) self.s = '' def clear_wrapped_lines(self): """Clear the wrapped lines of the current input.""" # curses does not handle this on its own. Sad. height, width = self.scr.getmaxyx() max_y = min(self.iy + (self.ix + len(self.s)) // width + 1, height) for y in xrange(self.iy + 1, max_y): self.scr.move(y, 0) self.scr.clrtoeol() def complete(self, tab=False): """Get Autcomplete list and window.""" if self.paste_mode and self.list_win_visible: self.scr.touchwin() if self.paste_mode: return if self.list_win_visible and not self.config.auto_display_list: self.scr.touchwin() self.list_win_visible = False self.matches_iter.update() return if self.config.auto_display_list or tab: self.list_win_visible = repl.Repl.complete(self, tab) if self.list_win_visible: try: self.show_list(self.matches, self.argspec) except curses.error: # XXX: This is a massive hack, it will go away when I get # cusswords into a good enough state that we can start # using it. self.list_win.border() self.list_win.refresh() self.list_win_visible = False if not self.list_win_visible: self.scr.redrawwin() self.scr.refresh() def clrtobol(self): """Clear from cursor to beginning of line; usual C-u behaviour""" self.clear_wrapped_lines() if not self.cpos: self.s = '' else: self.s = self.s[-self.cpos:] self.print_line(self.s, clr=True) self.scr.redrawwin() self.scr.refresh() def current_line(self): """Return the current line.""" return self.s def cut_to_buffer(self): """Clear from cursor to end of line, placing into cut buffer""" self.cut_buffer = self.s[-self.cpos:] self.s = self.s[:-self.cpos] self.cpos = 0 self.print_line(self.s, clr=True) self.scr.redrawwin() self.scr.refresh() def cw(self): """Return the current word, i.e. the (incomplete) word directly to the left of the cursor""" # I don't know if autocomplete should be disabled if the cursor # isn't at the end of the line, but that's what this does for now. if self.cpos: return # look from right to left for a bad method character l = len(self.s) is_method_char = lambda c: c.isalnum() or c in ('.', '_') if not self.s or not is_method_char(self.s[l-1]): return for i in range(1, l+1): if not is_method_char(self.s[-i]): i -= 1 break return self.s[-i:] def delete(self): """Process a del""" if not self.s: return if self.mvc(-1): self.bs(False) def echo(self, s, redraw=True): """Parse and echo a formatted string with appropriate attributes. It uses the formatting method as defined in formatter.py to parse the srings. It won't update the screen if it's reevaluating the code (as it does with undo).""" if not py3 and isinstance(s, unicode): s = s.encode(getpreferredencoding()) a = get_colpair(self.config, 'output') if '\x01' in s: rx = re.search('\x01([A-Za-z])([A-Za-z]?)', s) if rx: fg = rx.groups()[0] bg = rx.groups()[1] col_num = self._C[fg.lower()] if bg and bg != 'I': col_num *= self._C[bg.lower()] a = curses.color_pair(int(col_num) + 1) if bg == 'I': a = a | curses.A_REVERSE s = re.sub('\x01[A-Za-z][A-Za-z]?', '', s) if fg.isupper(): a = a | curses.A_BOLD s = s.replace('\x03', '') s = s.replace('\x01', '') # Replace NUL bytes, as addstr raises an exception otherwise s = s.replace('\0', '') # Replace \r\n bytes, as addstr remove the current line otherwise s = s.replace('\r\n', '\n') self.scr.addstr(s, a) if redraw and not self.evaluating: self.scr.refresh() def end(self, refresh=True): self.cpos = 0 h, w = gethw() y, x = divmod(len(self.s) + self.ix, w) y += self.iy self.scr.move(y, x) if refresh: self.scr.refresh() return True def hbegin(self): """Replace the active line with first line in history and increment the index to keep track""" self.cpos = 0 self.clear_wrapped_lines() self.rl_history.enter(self.s) self.s = self.rl_history.first() self.print_line(self.s, clr=True) def hend(self): """Same as hbegin() but, well, forward""" self.cpos = 0 self.clear_wrapped_lines() self.rl_history.enter(self.s) self.s = self.rl_history.last() self.print_line(self.s, clr=True) def back(self): """Replace the active line with previous line in history and increment the index to keep track""" self.cpos = 0 self.clear_wrapped_lines() self.rl_history.enter(self.s) self.s = self.rl_history.back() self.print_line(self.s, clr=True) def fwd(self): """Same as back() but, well, forward""" self.cpos = 0 self.clear_wrapped_lines() self.rl_history.enter(self.s) self.s = self.rl_history.forward() self.print_line(self.s, clr=True) def search(self): """Search with the partial matches from the history object.""" self.cpo = 0 self.clear_wrapped_lines() self.rl_history.enter(self.s) self.s = self.rl_history.back(start=False, search=True) self.print_line(self.s, clr=True) def get_key(self): key = '' while True: try: key += self.scr.getkey() if py3: # Seems like we get a in the locale's encoding # encoded string in Python 3 as well, but of # type str instead of bytes, hence convert it to # bytes first and decode then key = key.encode('latin-1').decode(getpreferredencoding()) else: key = key.decode(getpreferredencoding()) self.scr.nodelay(False) except UnicodeDecodeError: # Yes, that actually kind of sucks, but I don't see another way to get # input right self.scr.nodelay(True) except curses.error: # I'm quite annoyed with the ambiguity of this exception handler. I previously # caught "curses.error, x" and accessed x.message and checked that it was "no # input", which seemed a crappy way of doing it. But then I ran it on a # different computer and the exception seems to have entirely different # attributes. So let's hope getkey() doesn't raise any other crazy curses # exceptions. :) self.scr.nodelay(False) # XXX What to do here? Raise an exception? if key: return key else: if key != '\x00': t = time.time() self.paste_mode = ( t - self.last_key_press <= self.config.paste_time ) self.last_key_press = t return key else: key = '' finally: if self.idle: self.idle(self) def get_line(self): """Get a line of text and return it This function initialises an empty string and gets the curses cursor position on the screen and stores it for the echo() function to use later (I think). Then it waits for key presses and passes them to p_key(), which returns None if Enter is pressed (that means "Return", idiot).""" self.s = '' self.rl_history.reset() self.iy, self.ix = self.scr.getyx() if not self.paste_mode: for _ in xrange(self.next_indentation()): self.p_key('\t') self.cpos = 0 while True: key = self.get_key() if self.p_key(key) is None: if self.config.cli_trim_prompts and self.s.startswith(">>> "): self.s = self.s[4:] return self.s def home(self, refresh=True): self.scr.move(self.iy, self.ix) self.cpos = len(self.s) if refresh: self.scr.refresh() return True def lf(self): """Process a linefeed character; it only needs to check the cursor position and move appropriately so it doesn't clear the current line after the cursor.""" if self.cpos: for _ in range(self.cpos): self.mvc(-1) # Reprint the line (as there was maybe a highlighted paren in it) self.print_line(self.s, newline=True) self.echo("\n") def mkargspec(self, topline, down): """This figures out what to do with the argspec and puts it nicely into the list window. It returns the number of lines used to display the argspec. It's also kind of messy due to it having to call so many addstr() to get the colouring right, but it seems to be pretty sturdy.""" r = 3 fn = topline[0] args = topline[1][0] kwargs = topline[1][3] _args = topline[1][1] _kwargs = topline[1][2] is_bound_method = topline[2] in_arg = topline[3] if py3: kwonly = topline[1][4] kwonly_defaults = topline[1][5] or dict() max_w = int(self.scr.getmaxyx()[1] * 0.6) self.list_win.erase() self.list_win.resize(3, max_w) h, w = self.list_win.getmaxyx() self.list_win.addstr('\n ') self.list_win.addstr(fn, get_colpair(self.config, 'name') | curses.A_BOLD) self.list_win.addstr(': (', get_colpair(self.config, 'name')) maxh = self.scr.getmaxyx()[0] if is_bound_method and isinstance(in_arg, int): in_arg += 1 punctuation_colpair = get_colpair(self.config, 'punctuation') for k, i in enumerate(args): y, x = self.list_win.getyx() ln = len(str(i)) kw = None if kwargs and k + 1 > len(args) - len(kwargs): kw = repr(kwargs[k - (len(args) - len(kwargs))]) ln += len(kw) + 1 if ln + x >= w: ty = self.list_win.getbegyx()[0] if not down and ty > 0: h += 1 self.list_win.mvwin(ty - 1, 1) self.list_win.resize(h, w) elif down and h + r < maxh - ty: h += 1 self.list_win.resize(h, w) else: break r += 1 self.list_win.addstr('\n\t') if str(i) == 'self' and k == 0: color = get_colpair(self.config, 'name') else: color = get_colpair(self.config, 'token') if k == in_arg or i == in_arg: color |= curses.A_BOLD if not py3: # See issue #138: We need to format tuple unpacking correctly # We use the undocumented function inspection.strseq() for # that. Fortunately, that madness is gone in Python 3. self.list_win.addstr(inspect.strseq(i, str), color) else: self.list_win.addstr(str(i), color) if kw is not None: self.list_win.addstr('=', punctuation_colpair) self.list_win.addstr(kw, get_colpair(self.config, 'token')) if k != len(args) -1: self.list_win.addstr(', ', punctuation_colpair) if _args: if args: self.list_win.addstr(', ', punctuation_colpair) self.list_win.addstr('*%s' % (_args, ), get_colpair(self.config, 'token')) if py3 and kwonly: if not _args: if args: self.list_win.addstr(', ', punctuation_colpair) self.list_win.addstr('*', punctuation_colpair) marker = object() for arg in kwonly: self.list_win.addstr(', ', punctuation_colpair) color = get_colpair(self.config, 'token') if arg == in_arg: color |= curses.A_BOLD self.list_win.addstr(arg, color) default = kwonly_defaults.get(arg, marker) if default is not marker: self.list_win.addstr('=', punctuation_colpair) self.list_win.addstr(repr(default), get_colpair(self.config, 'token')) if _kwargs: if args or _args or (py3 and kwonly): self.list_win.addstr(', ', punctuation_colpair) self.list_win.addstr('**%s' % (_kwargs, ), get_colpair(self.config, 'token')) self.list_win.addstr(')', punctuation_colpair) return r def mvc(self, i, refresh=True): """This method moves the cursor relatively from the current position, where: 0 == (right) end of current line length of current line len(self.s) == beginning of current line and: current cursor position + i for positive values of i the cursor will move towards the beginning of the line, negative values the opposite.""" y, x = self.scr.getyx() if self.cpos == 0 and i < 0: return False if x == self.ix and y == self.iy and i >= 1: return False h, w = gethw() if x - i < 0: y -= 1 x = w if x - i >= w: y += 1 x = 0 + i self.cpos += i self.scr.move(y, x - i) if refresh: self.scr.refresh() return True def p_key(self, key): """Process a keypress""" if key is None: return '' config = self.config if platform.system() == 'Windows': C_BACK = chr(127) BACKSP = chr(8) else: C_BACK = chr(8) BACKSP = chr(127) if key == C_BACK: # C-Backspace (on my computer anyway!) self.clrtobol() key = '\n' # Don't return; let it get handled if key == chr(27): #Escape Key return '' if key in (BACKSP, 'KEY_BACKSPACE'): self.bs() self.complete() return '' elif key in key_dispatch[config.delete_key] and not self.s: # Delete on empty line exits self.do_exit = True return None elif key in ('KEY_DC', ) + key_dispatch[config.delete_key]: self.delete() self.complete() # Redraw (as there might have been highlighted parens) self.print_line(self.s) return '' elif key in key_dispatch[config.undo_key]: # C-r self.undo() return '' elif key in key_dispatch[config.search_key]: self.search() return '' elif key in ('KEY_UP', ) + key_dispatch[config.up_one_line_key]: # Cursor Up/C-p self.back() return '' elif key in ('KEY_DOWN', ) + key_dispatch[config.down_one_line_key]: # Cursor Down/C-n self.fwd() return '' elif key in ("KEY_LEFT",' ^B', chr(2)): # Cursor Left or ^B self.mvc(1) # Redraw (as there might have been highlighted parens) self.print_line(self.s) elif key in ("KEY_RIGHT", '^F', chr(6)): # Cursor Right or ^F self.mvc(-1) # Redraw (as there might have been highlighted parens) self.print_line(self.s) elif key in ("KEY_HOME", '^A', chr(1)): # home or ^A self.home() # Redraw (as there might have been highlighted parens) self.print_line(self.s) elif key in ("KEY_END", '^E', chr(5)): # end or ^E self.end() # Redraw (as there might have been highlighted parens) self.print_line(self.s) elif key in ("KEY_NPAGE", '\T'): # page_down or \T self.hend() self.print_line(self.s) elif key in ("KEY_PPAGE", '\S'): # page_up or \S self.hbegin() self.print_line(self.s) elif key in key_dispatch[config.cut_to_buffer_key]: # cut to buffer self.cut_to_buffer() return '' elif key in key_dispatch[config.yank_from_buffer_key]: # yank from buffer self.yank_from_buffer() return '' elif key in key_dispatch[config.clear_word_key]: self.cut_buffer = self.bs_word() self.complete() return '' elif key in key_dispatch[config.clear_line_key]: self.clrtobol() return '' elif key in key_dispatch[config.clear_screen_key]: self.s_hist = [self.s_hist[-1]] self.highlighted_paren = None self.redraw() return '' elif key in key_dispatch[config.exit_key]: if not self.s: self.do_exit = True return None else: return '' elif key in key_dispatch[config.save_key]: self.write2file() return '' elif key in key_dispatch[config.pastebin_key]: self.pastebin() return '' elif key in key_dispatch[config.last_output_key]: page(self.stdout_hist[self.prev_block_finished:-4]) return '' elif key in key_dispatch[config.show_source_key]: source = self.get_source_of_current_name() if source is not None: if config.highlight_show_source: source = format(PythonLexer().get_tokens(source), TerminalFormatter()) page(source) else: self.statusbar.message(_('Cannot show source.')) return '' elif key in ('\n', '\r', 'PADENTER'): self.lf() return None elif key == '\t': return self.tab() elif key == 'KEY_BTAB': return self.tab(back=True) elif key in key_dispatch[config.suspend_key]: if platform.system() != 'Windows': self.suspend() return '' else: self.do_exit = True return None elif key[0:3] == 'PAD' and not key in ('PAD0', 'PADSTOP'): pad_keys = { 'PADMINUS': '-', 'PADPLUS': '+', 'PADSLASH': '/', 'PADSTAR': '*', } try: self.addstr(pad_keys[key]) self.print_line(self.s) except KeyError: return '' elif len(key) == 1 and not unicodedata.category(key) == 'Cc': self.addstr(key) self.print_line(self.s) else: return '' return True def print_line(self, s, clr=False, newline=False): """Chuck a line of text through the highlighter, move the cursor to the beginning of the line and output it to the screen.""" if not s: clr = True if self.highlighted_paren is not None: # Clear previous highlighted paren self.reprint_line(*self.highlighted_paren) self.highlighted_paren = None if self.config.syntax and (not self.paste_mode or newline): o = format(self.tokenize(s, newline), self.formatter) else: o = s self.f_string = o self.scr.move(self.iy, self.ix) if clr: self.scr.clrtoeol() if clr and not s: self.scr.refresh() if o: for t in o.split('\x04'): self.echo(t.rstrip('\n')) if self.cpos: t = self.cpos for _ in range(self.cpos): self.mvc(1) self.cpos = t def prompt(self, more): """Show the appropriate Python prompt""" if not more: self.echo("\x01%s\x03%s" % (self.config.color_scheme['prompt'], self.ps1)) self.stdout_hist += self.ps1 self.s_hist.append('\x01%s\x03%s\x04' % (self.config.color_scheme['prompt'], self.ps1)) else: prompt_more_color = self.config.color_scheme['prompt_more'] self.echo("\x01%s\x03%s" % (prompt_more_color, self.ps2)) self.stdout_hist += self.ps2 self.s_hist.append('\x01%s\x03%s\x04' % (prompt_more_color, self.ps2)) def push(self, s, insert_into_history=True): # curses.raw(True) prevents C-c from causing a SIGINT curses.raw(False) try: return repl.Repl.push(self, s, insert_into_history) except SystemExit, e: # Avoid a traceback on e.g. quit() self.do_exit = True self.exit_value = e.args return False finally: curses.raw(True) def redraw(self): """Redraw the screen.""" self.scr.erase() for k, s in enumerate(self.s_hist): if not s: continue self.iy, self.ix = self.scr.getyx() for i in s.split('\x04'): self.echo(i, redraw=False) if k < len(self.s_hist) -1: self.scr.addstr('\n') self.iy, self.ix = self.scr.getyx() self.print_line(self.s) self.scr.refresh() self.statusbar.refresh() def repl(self): """Initialise the repl and jump into the loop. This method also has to keep a stack of lines entered for the horrible "undo" feature. It also tracks everything that would normally go to stdout in the normal Python interpreter so it can quickly write it to stdout on exit after curses.endwin(), as well as a history of lines entered for using up/down to go back and forth (which has to be separate to the evaluation history, which will be truncated when undoing.""" # Use our own helper function because Python's will use real stdin and # stdout instead of our wrapped self.push('from bpython._internal import _help as help\n', False) self.iy, self.ix = self.scr.getyx() more = False while not self.do_exit: self.f_string = '' self.prompt(more) try: inp = self.get_line() except KeyboardInterrupt: self.statusbar.message('KeyboardInterrupt') self.scr.addstr('\n') self.scr.touchwin() self.scr.refresh() continue self.scr.redrawwin() if self.do_exit: return self.exit_value self.history.append(inp) self.s_hist[-1] += self.f_string if py3: self.stdout_hist += inp + '\n' else: self.stdout_hist += inp.encode(getpreferredencoding()) + '\n' stdout_position = len(self.stdout_hist) more = self.push(inp) if not more: self.prev_block_finished = stdout_position self.s = '' return self.exit_value def reprint_line(self, lineno, tokens): """Helper function for paren highlighting: Reprint line at offset `lineno` in current input buffer.""" if not self.buffer or lineno == len(self.buffer): return real_lineno = self.iy height, width = self.scr.getmaxyx() for i in xrange(lineno, len(self.buffer)): string = self.buffer[i] # 4 = length of prompt length = len(string.encode(getpreferredencoding())) + 4 real_lineno -= int(math.ceil(length / width)) if real_lineno < 0: return self.scr.move(real_lineno, len(self.ps1) if lineno == 0 else len(self.ps2)) line = format(tokens, BPythonFormatter(self.config.color_scheme)) for string in line.split('\x04'): self.echo(string) def resize(self): """This method exists simply to keep it straight forward when initialising a window and resizing it.""" self.size() self.scr.erase() self.scr.resize(self.h, self.w) self.scr.mvwin(self.y, self.x) self.statusbar.resize(refresh=False) self.redraw() def getstdout(self): """This method returns the 'spoofed' stdout buffer, for writing to a file or sending to a pastebin or whatever.""" return self.stdout_hist + '\n' def reevaluate(self): """Clear the buffer, redraw the screen and re-evaluate the history""" self.evaluating = True self.stdout_hist = '' self.f_string = '' self.buffer = [] self.scr.erase() self.s_hist = [] # Set cursor position to -1 to prevent paren matching self.cpos = -1 self.prompt(False) self.iy, self.ix = self.scr.getyx() for line in self.history: if py3: self.stdout_hist += line + '\n' else: self.stdout_hist += line.encode(getpreferredencoding()) + '\n' self.print_line(line) self.s_hist[-1] += self.f_string # I decided it was easier to just do this manually # than to make the print_line and history stuff more flexible. self.scr.addstr('\n') more = self.push(line) self.prompt(more) self.iy, self.ix = self.scr.getyx() self.cpos = 0 indent = repl.next_indentation(self.s, self.config.tab_length) self.s = '' self.scr.refresh() if self.buffer: for _ in xrange(indent): self.tab() self.evaluating = False #map(self.push, self.history) #^-- That's how simple this method was at first :( def write(self, s): """For overriding stdout defaults""" if '\x04' in s: for block in s.split('\x04'): self.write(block) return if s.rstrip() and '\x03' in s: t = s.split('\x03')[1] else: t = s if not py3 and isinstance(t, unicode): t = t.encode(getpreferredencoding()) if not self.stdout_hist: self.stdout_hist = t else: self.stdout_hist += t self.echo(s) self.s_hist.append(s.rstrip()) def show_list(self, items, topline=None, current_item=None): shared = Struct() shared.cols = 0 shared.rows = 0 shared.wl = 0 y, x = self.scr.getyx() h, w = self.scr.getmaxyx() down = (y < h // 2) if down: max_h = h - y else: max_h = y + 1 max_w = int(w * self.config.cli_suggestion_width) self.list_win.erase() if items: sep = '.' if os.path.sep in items[0]: # Filename completion sep = os.path.sep if sep in items[0]: items = [x.rstrip(sep).rsplit(sep)[-1] for x in items] if current_item: current_item = current_item.rstrip(sep).rsplit(sep)[-1] if topline: height_offset = self.mkargspec(topline, down) + 1 else: height_offset = 0 def lsize(): wl = max(len(i) for i in v_items) + 1 if not wl: wl = 1 cols = ((max_w - 2) // wl) or 1 rows = len(v_items) // cols if cols * rows < len(v_items): rows += 1 if rows + 2 >= max_h: rows = max_h - 2 return False shared.rows = rows shared.cols = cols shared.wl = wl return True if items: # visible items (we'll append until we can't fit any more in) v_items = [items[0][:max_w - 3]] lsize() else: v_items = [] for i in items[1:]: v_items.append(i[:max_w - 3]) if not lsize(): del v_items[-1] v_items[-1] = '...' break rows = shared.rows if rows + height_offset < max_h: rows += height_offset display_rows = rows else: display_rows = rows + height_offset cols = shared.cols wl = shared.wl if topline and not v_items: w = max_w elif wl + 3 > max_w: w = max_w else: t = (cols + 1) * wl + 3 if t > max_w: t = max_w w = t if height_offset and display_rows + 5 >= max_h: del v_items[-(cols * (height_offset)):] if self.docstring is None: self.list_win.resize(rows + 2, w) else: docstring = self.format_docstring(self.docstring, max_w - 2, max_h - height_offset) docstring_string = ''.join(docstring) rows += len(docstring) self.list_win.resize(rows, max_w) if down: self.list_win.mvwin(y + 1, 0) else: self.list_win.mvwin(y - rows - 2, 0) if v_items: self.list_win.addstr('\n ') if not py3: encoding = getpreferredencoding() for ix, i in enumerate(v_items): padding = (wl - len(i)) * ' ' if i == current_item: color = get_colpair(self.config, 'operator') else: color = get_colpair(self.config, 'main') if not py3: i = i.encode(encoding) self.list_win.addstr(i + padding, color) if ((cols == 1 or (ix and not (ix + 1) % cols)) and ix + 1 < len(v_items)): self.list_win.addstr('\n ') if self.docstring is not None: if not py3 and isinstance(docstring_string, unicode): docstring_string = docstring_string.encode(encoding, 'ignore') self.list_win.addstr('\n' + docstring_string, get_colpair(self.config, 'comment')) # XXX: After all the trouble I had with sizing the list box (I'm not very good # at that type of thing) I decided to do this bit of tidying up here just to # make sure there's no unnececessary blank lines, it makes things look nicer. y = self.list_win.getyx()[0] self.list_win.resize(y + 2, w) self.statusbar.win.touchwin() self.statusbar.win.noutrefresh() self.list_win.attron(get_colpair(self.config, 'main')) self.list_win.border() self.scr.touchwin() self.scr.cursyncup() self.scr.noutrefresh() # This looks a little odd, but I can't figure a better way to stick the cursor # back where it belongs (refreshing the window hides the list_win) self.scr.move(*self.scr.getyx()) self.list_win.refresh() def size(self): """Set instance attributes for x and y top left corner coordinates and width and heigth for the window.""" global stdscr h, w = stdscr.getmaxyx() self.y = 0 self.w = w self.h = h - 1 self.x = 0 def suspend(self): """Suspend the current process for shell job control.""" if platform.system() != 'Windows': curses.endwin() os.kill(os.getpid(), signal.SIGSTOP) def tab(self, back=False): """Process the tab key being hit. If there's only whitespace in the line or the line is blank then process a normal tab, otherwise attempt to autocomplete to the best match of possible choices in the match list. If `back` is True, walk backwards through the list of suggestions and don't indent if there are only whitespace in the line. """ mode = self.config.autocomplete_mode # 1. check if we should add a tab character if self.atbol() and not back: x_pos = len(self.s) - self.cpos num_spaces = x_pos % self.config.tab_length if not num_spaces: num_spaces = self.config.tab_length self.addstr(' ' * num_spaces) self.print_line(self.s) return True # 2. get the current word if not self.matches_iter: self.complete(tab=True) if not self.config.auto_display_list and not self.list_win_visible: return True cw = self.current_string() or self.cw() if not cw: return True else: cw = self.matches_iter.current_word # 3. check to see if we can expand the current word cseq = None if mode == autocomplete.SUBSTRING: if all([len(match.split(cw)) == 2 for match in self.matches]): seq = [cw + match.split(cw)[1] for match in self.matches] cseq = os.path.commonprefix(seq) else: seq = self.matches cseq = os.path.commonprefix(seq) if cseq and mode != autocomplete.FUZZY: expanded_string = cseq[len(cw):] self.s += expanded_string expanded = bool(expanded_string) self.print_line(self.s) if len(self.matches) == 1 and self.config.auto_display_list: self.scr.touchwin() if expanded: self.matches_iter.update(cseq, self.matches) else: expanded = False # 4. swap current word for a match list item if not expanded and self.matches: # reset s if this is the nth result if self.matches_iter: self.s = self.s[:-len(self.matches_iter.current())] + cw current_match = back and self.matches_iter.previous() \ or self.matches_iter.next() # update s with the new match if current_match: try: self.show_list(self.matches, self.argspec, current_match) except curses.error: # XXX: This is a massive hack, it will go away when I get # cusswords into a good enough state that we can start # using it. self.list_win.border() self.list_win.refresh() if self.config.autocomplete_mode == autocomplete.SIMPLE: self.s += current_match[len(cw):] else: self.s = self.s[:-len(cw)] + current_match self.print_line(self.s, True) return True def undo(self, n=1): repl.Repl.undo(self, n) # This will unhighlight highlighted parens self.print_line(self.s) def writetb(self, lines): for line in lines: self.write('\x01%s\x03%s' % (self.config.color_scheme['error'], line)) def yank_from_buffer(self): """Paste the text from the cut buffer at the current cursor location""" self.addstr(self.cut_buffer) self.print_line(self.s, clr=True) class Statusbar(object): """This class provides the status bar at the bottom of the screen. It has message() and prompt() methods for user interactivity, as well as settext() and clear() methods for changing its appearance. The check() method needs to be called repeatedly if the statusbar is going to be aware of when it should update its display after a message() has been called (it'll display for a couple of seconds and then disappear). It should be called as: foo = Statusbar(stdscr, scr, 'Initial text to display') or, for a blank statusbar: foo = Statusbar(stdscr, scr) It can also receive the argument 'c' which will be an integer referring to a curses colour pair, e.g.: foo = Statusbar(stdscr, 'Hello', c=4) stdscr should be a curses window object in which to put the status bar. pwin should be the parent window. To be honest, this is only really here so the cursor can be returned to the window properly. """ def __init__(self, scr, pwin, background, config, s=None, c=None): """Initialise the statusbar and display the initial text (if any)""" self.size() self.win = newwin(background, self.h, self.w, self.y, self.x) self.config = config self.s = s or '' self._s = self.s self.c = c self.timer = 0 self.pwin = pwin self.settext(s, c) def size(self): """Set instance attributes for x and y top left corner coordinates and width and heigth for the window.""" h, w = gethw() self.y = h - 1 self.w = w self.h = 1 self.x = 0 def resize(self, refresh=True): """This method exists simply to keep it straight forward when initialising a window and resizing it.""" self.size() self.win.mvwin(self.y, self.x) self.win.resize(self.h, self.w) if refresh: self.refresh() def refresh(self): """This is here to make sure the status bar text is redraw properly after a resize.""" self.settext(self._s) def check(self): """This is the method that should be called every half second or so to see if the status bar needs updating.""" if not self.timer: return if time.time() < self.timer: return self.settext(self._s) def message(self, s, n=3): """Display a message for a short n seconds on the statusbar and return it to its original state.""" self.timer = time.time() + n self.settext(s) def prompt(self, s=''): """Prompt the user for some input (with the optional prompt 's') and return the input text, then restore the statusbar to its original value.""" self.settext(s or '? ', p=True) iy, ix = self.win.getyx() def bs(s): y, x = self.win.getyx() if x == ix: return s s = s[:-1] self.win.delch(y, x - 1) self.win.move(y, x - 1) return s o = '' while True: c = self.win.getch() # '\b' if c == 127: o = bs(o) # '\n' elif c == 10: break # ESC elif c == 27: curses.flushinp() raise ValueError # literal elif 0 <= c < 127: c = chr(c) self.win.addstr(c, get_colpair(self.config, 'prompt')) o += c self.settext(self._s) return o def settext(self, s, c=None, p=False): """Set the text on the status bar to a new permanent value; this is the value that will be set after a prompt or message. c is the optional curses colour pair to use (if not specified the last specified colour pair will be used). p is True if the cursor is expected to stay in the status window (e.g. when prompting).""" self.win.erase() if len(s) >= self.w: s = s[:self.w - 1] self.s = s if c: self.c = c if s: if not py3 and isinstance(s, unicode): s = s.encode(getpreferredencoding()) if self.c: self.win.addstr(s, self.c) else: self.win.addstr(s) if not p: self.win.noutrefresh() self.pwin.refresh() else: self.win.refresh() def clear(self): """Clear the status bar.""" self.win.clear() def init_wins(scr, config): """Initialise the two windows (the main repl interface and the little status bar at the bottom with some stuff in it)""" #TODO: Document better what stuff is on the status bar. background = get_colpair(config, 'background') h, w = gethw() main_win = newwin(background, h - 1, w, 0, 0) main_win.scrollok(True) main_win.keypad(1) # Thanks to Angus Gibson for pointing out this missing line which was causing # problems that needed dirty hackery to fix. :) statusbar = Statusbar(scr, main_win, background, config, _(" <%s> Rewind <%s> Save <%s> Pastebin " " <%s> Pager <%s> Show Source ") % (config.undo_key, config.save_key, config.pastebin_key, config.last_output_key, config.show_source_key), get_colpair(config, 'main')) return main_win, statusbar def sigwinch(unused_scr): global DO_RESIZE DO_RESIZE = True def sigcont(unused_scr): sigwinch(unused_scr) # Forces the redraw curses.ungetch('\x00') def gethw(): """I found this code on a usenet post, and snipped out the bit I needed, so thanks to whoever wrote that, sorry I forgot your name, I'm sure you're a great guy. It's unfortunately necessary (unless someone has any better ideas) in order to allow curses and readline to work together. I looked at the code for libreadline and noticed this comment: /* This is the stuff that is hard for me. I never seem to write good display routines in C. Let's see how I do this time. */ So I'm not going to ask any questions. """ if platform.system() != 'Windows': h, w = struct.unpack( "hhhh", fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000" * 8))[0:2] else: from ctypes import windll, create_string_buffer # stdin handle is -10 # stdout handle is -11 # stderr handle is -12 h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) if res: (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 else: sizex, sizey = stdscr.getmaxyx()# can't determine actual size - return default values h, w = sizey, sizex return h, w def idle(caller): """This is called once every iteration through the getkey() loop (currently in the Repl class, see the get_line() method). The statusbar check needs to go here to take care of timed messages and the resize handlers need to be here to make sure it happens conveniently.""" global DO_RESIZE if importcompletion.find_coroutine() or caller.paste_mode: caller.scr.nodelay(True) key = caller.scr.getch() caller.scr.nodelay(False) if key != -1: curses.ungetch(key) else: curses.ungetch('\x00') caller.statusbar.check() caller.check() if DO_RESIZE: do_resize(caller) def do_resize(caller): """This needs to hack around readline and curses not playing nicely together. See also gethw() above.""" global DO_RESIZE h, w = gethw() if not h: # Hopefully this shouldn't happen. :) return curses.endwin() os.environ["LINES"] = str(h) os.environ["COLUMNS"] = str(w) curses.doupdate() DO_RESIZE = False caller.resize() # The list win resizes itself every time it appears so no need to do it here. class FakeDict(object): """Very simple dict-alike that returns a constant value for any key - used as a hacky solution to using a colours dict containing colour codes if colour initialisation fails.""" def __init__(self, val): self._val = val def __getitem__(self, k): return self._val def newwin(background, *args): """Wrapper for curses.newwin to automatically set background colour on any newly created window.""" win = curses.newwin(*args) win.bkgd(' ', background) return win def curses_wrapper(func, *args, **kwargs): """Like curses.wrapper(), but reuses stdscr when called again.""" global stdscr if stdscr is None: stdscr = curses.initscr() try: curses.noecho() curses.cbreak() stdscr.keypad(1) try: curses.start_color() except curses.error: pass return func(stdscr, *args, **kwargs) finally: stdscr.keypad(0) curses.echo() curses.nocbreak() curses.endwin() def main_curses(scr, args, config, interactive=True, locals_=None, banner=None): """main function for the curses convenience wrapper Initialise the two main objects: the interpreter and the repl. The repl does what a repl does and lots of other cool stuff like syntax highlighting and stuff. I've tried to keep it well factored but it needs some tidying up, especially in separating the curses stuff from the rest of the repl. Returns a tuple (exit value, output), where exit value is a tuple with arguments passed to SystemExit. """ global stdscr global DO_RESIZE global colors DO_RESIZE = False if platform.system() != 'Windows': old_sigwinch_handler = signal.signal(signal.SIGWINCH, lambda *_: sigwinch(scr)) # redraw window after being suspended old_sigcont_handler = signal.signal(signal.SIGCONT, lambda *_: sigcont(scr)) stdscr = scr try: curses.start_color() curses.use_default_colors() cols = make_colors(config) except curses.error: cols = FakeDict(-1) # FIXME: Gargh, bad design results in using globals without a refactor :( colors = cols scr.timeout(300) curses.raw(True) main_win, statusbar = init_wins(scr, config) if locals_ is None: sys.modules['__main__'] = ModuleType('__main__') locals_ = sys.modules['__main__'].__dict__ interpreter = repl.Interpreter(locals_, getpreferredencoding()) clirepl = CLIRepl(main_win, interpreter, statusbar, config, idle) clirepl._C = cols sys.stdin = FakeStdin(clirepl) sys.stdout = FakeStream(clirepl) sys.stderr = FakeStream(clirepl) if args: bpython.args.exec_code(interpreter, args) if not interactive: curses.raw(False) return clirepl.getstdout() else: sys.path.insert(0, '') clirepl.startup() if banner is not None: clirepl.write(banner) clirepl.write('\n') exit_value = clirepl.repl() main_win.erase() main_win.refresh() statusbar.win.clear() statusbar.win.refresh() curses.raw(False) # Restore signal handlers if platform.system() != 'Windows': signal.signal(signal.SIGWINCH, old_sigwinch_handler) signal.signal(signal.SIGCONT, old_sigcont_handler) return (exit_value, clirepl.getstdout()) def main(args=None, locals_=None, banner=None): translations.init() config, options, exec_args = bpython.args.parse(args) # Save stdin, stdout and stderr for later restoration orig_stdin = sys.stdin orig_stdout = sys.stdout orig_stderr = sys.stderr try: (exit_value, output) = curses_wrapper( main_curses, exec_args, config, options.interactive, locals_, banner=banner) finally: sys.stdin = orig_stdin sys.stderr = orig_stderr sys.stdout = orig_stdout # Fake stdout data so everything's still visible after exiting if config.flush_output and not options.quiet: sys.stdout.write(output) if hasattr(sys.stdout, 'flush'): sys.stdout.flush() return repl.extract_exit_value(exit_value) if __name__ == '__main__': from bpython.cli import main sys.exit(main()) # vim: sw=4 ts=4 sts=4 ai et bpython-0.12/bpython/__init__.py0000644000175000017500000000247012061402702017204 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Bob Farrell # # 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. import os.path __version__ = '0.12' package_dir = os.path.abspath(os.path.dirname(__file__)) def embed(locals_=None, args=['-i', '-q'], banner=None): from bpython.cli import main return main(args, locals_, banner) bpython-0.12/bpython/urwid.py0000644000175000017500000014263412061377417016624 0ustar simonsimon00000000000000 # # The MIT License # # Copyright (c) 2010-2011 Marien Zwart # # 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. """bpython backend based on Urwid. Based on Urwid 0.9.9. This steals many things from bpython's "cli" backend. This is still *VERY* rough. """ from __future__ import absolute_import, with_statement, division import sys import os import time import locale import signal from types import ModuleType from optparse import Option from pygments.token import Token from bpython import args as bpargs, repl, translations from bpython._py3compat import py3 from bpython.formatter import theme_map from bpython.importcompletion import find_coroutine from bpython.translations import _ from bpython.keys import urwid_key_dispatch as key_dispatch import urwid if not py3: import inspect Parenthesis = Token.Punctuation.Parenthesis # Urwid colors are: # 'black', 'dark red', 'dark green', 'brown', 'dark blue', # 'dark magenta', 'dark cyan', 'light gray', 'dark gray', # 'light red', 'light green', 'yellow', 'light blue', # 'light magenta', 'light cyan', 'white' # and bpython has: # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default COLORMAP = { 'k': 'black', 'r': 'dark red', # or light red? 'g': 'dark green', # or light green? 'y': 'yellow', 'b': 'dark blue', # or light blue? 'm': 'dark magenta', # or light magenta? 'c': 'dark cyan', # or light cyan? 'w': 'white', 'd': 'default', } # Add our keys to the urwid command_map try: from twisted.internet import protocol from twisted.protocols import basic except ImportError: pass else: class EvalProtocol(basic.LineOnlyReceiver): delimiter = '\n' def __init__(self, myrepl): self.repl = myrepl def lineReceived(self, line): # HACK! # TODO: deal with encoding issues here... self.repl.main_loop.process_input(line) self.repl.main_loop.process_input(['enter']) class EvalFactory(protocol.ServerFactory): def __init__(self, myrepl): self.repl = myrepl def buildProtocol(self, addr): return EvalProtocol(self.repl) # If Twisted is not available urwid has no TwistedEventLoop attribute. # Code below will try to import reactor before using TwistedEventLoop. # I assume TwistedEventLoop will be available if that import succeeds. if urwid.VERSION < (1, 0, 0) and hasattr(urwid, 'TwistedEventLoop'): class TwistedEventLoop(urwid.TwistedEventLoop): """TwistedEventLoop modified to properly stop the reactor. urwid 0.9.9 and 0.9.9.1 crash the reactor on ExitMainLoop instead of stopping it. One obvious way this breaks is if anything used the reactor's thread pool: that thread pool is not shut down if the reactor is not stopped, which means python hangs on exit (joining the non-daemon threadpool threads that never exit). And the default resolver is the ThreadedResolver, so if we looked up any names we hang on exit. That is bad enough that we hack up urwid a bit here to exit properly. """ def handle_exit(self, f): def wrapper(*args, **kwargs): try: return f(*args, **kwargs) except urwid.ExitMainLoop: # This is our change. self.reactor.stop() except: # This is the same as in urwid. # We are obviously not supposed to ever hit this. import sys print sys.exc_info() self._exc_info = sys.exc_info() self.reactor.crash() return wrapper else: TwistedEventLoop = getattr(urwid, 'TwistedEventLoop', None) class StatusbarEdit(urwid.Edit): """Wrapper around urwid.Edit used for the prompt in Statusbar. This class only adds a single signal that is emitted if the user presses Enter.""" signals = urwid.Edit.signals + ['prompt_enter'] def __init__(self, *args, **kwargs): self.single = False urwid.Edit.__init__(self, *args, **kwargs) def keypress(self, size, key): if self.single: urwid.emit_signal(self, 'prompt_enter', self, key) elif key == 'enter': urwid.emit_signal(self, 'prompt_enter', self, self.get_edit_text()) else: return urwid.Edit.keypress(self, size, key) urwid.register_signal(StatusbarEdit, 'prompt_enter') class Statusbar(object): """Statusbar object, ripped off from bpython.cli. This class provides the status bar at the bottom of the screen. It has message() and prompt() methods for user interactivity, as well as settext() and clear() methods for changing its appearance. The check() method needs to be called repeatedly if the statusbar is going to be aware of when it should update its display after a message() has been called (it'll display for a couple of seconds and then disappear). It should be called as: foo = Statusbar('Initial text to display') or, for a blank statusbar: foo = Statusbar() The "widget" attribute is an urwid widget. """ signals = ['prompt_result'] def __init__(self, config, s=None, main_loop=None): self.config = config self.timer = None self.main_loop = main_loop self.s = s or '' self.text = urwid.Text(('main', self.s)) # use wrap mode 'clip' to just cut off at the end of line self.text.set_wrap_mode('clip') self.edit = StatusbarEdit(('main', '')) urwid.connect_signal(self.edit, 'prompt_enter', self._on_prompt_enter) self.widget = urwid.Columns([self.text, self.edit]) def _check(self, callback, userdata=None): """This is the method is called from the timer to reset the status bar.""" self.timer = None self.settext(self.s) def message(self, s, n=3): """Display a message for a short n seconds on the statusbar and return it to its original state.""" self.settext(s) self.timer = self.main_loop.set_alarm_in(n, self._check) def _reset_timer(self): """Reset the timer from message.""" if self.timer is not None: self.main_loop.remove_alarm(self.timer) self.timer = None def prompt(self, s=None, single=False): """Prompt the user for some input (with the optional prompt 's'). After the user hit enter the signal 'prompt_result' will be emited and the status bar will be reset. If single is True, the first keypress will be returned.""" self._reset_timer() self.edit.single = single self.edit.set_caption(('main', s or '?')) self.edit.set_edit_text('') # hide the text and display the edit widget if not self.edit in self.widget.widget_list: self.widget.widget_list.append(self.edit) if self.text in self.widget.widget_list: self.widget.widget_list.remove(self.text) self.widget.set_focus_column(0) def settext(self, s, permanent=False): """Set the text on the status bar to a new value. If permanent is True, the new value will be permanent. If that status bar is in prompt mode, the prompt will be aborted. """ self._reset_timer() # hide the edit and display the text widget if self.edit in self.widget.widget_list: self.widget.widget_list.remove(self.edit) if not self.text in self.widget.widget_list: self.widget.widget_list.append(self.text) self.text.set_text(('main', s)) if permanent: self.s = s def clear(self): """Clear the status bar.""" self.settext('') def _on_prompt_enter(self, edit, new_text): """Reset the statusbar and pass the input from the prompt to the caller via 'prompt_result'.""" self.settext(self.s) urwid.emit_signal(self, 'prompt_result', new_text) urwid.register_signal(Statusbar, 'prompt_result') def decoding_input_filter(keys, raw): """Input filter for urwid which decodes each key with the locale's preferred encoding.'""" encoding = locale.getpreferredencoding() converted_keys = list() for key in keys: if isinstance(key, basestring): converted_keys.append(key.decode(encoding)) else: converted_keys.append(key) return converted_keys def format_tokens(tokensource): for token, text in tokensource: if text == '\n': continue # TODO: something about inversing Parenthesis while token not in theme_map: token = token.parent yield (theme_map[token], text) class BPythonEdit(urwid.Edit): """Customized editor *very* tightly interwoven with URWIDRepl. Changes include: - The edit text supports markup, not just the caption. This works by calling set_edit_markup from the change event as well as whenever markup changes while text does not. - The widget can be made readonly, which currently just means it is no longer selectable and stops drawing the cursor. This is currently a one-way operation, but that is just because I only need and test the readwrite->readonly transition. - move_cursor_to_coords is ignored (except for internal calls from keypress or mouse_event). - arrow up/down are ignored. - an "edit-pos-changed" signal is emitted when edit_pos changes. """ signals = ['edit-pos-changed'] def __init__(self, config, *args, **kwargs): self._bpy_text = '' self._bpy_attr = [] self._bpy_selectable = True self._bpy_may_move_cursor = False self.config = config self.tab_length = config.tab_length urwid.Edit.__init__(self, *args, **kwargs) def set_edit_pos(self, pos): urwid.Edit.set_edit_pos(self, pos) self._emit("edit-pos-changed", self.edit_pos) def get_edit_pos(self): return self._edit_pos edit_pos = property(get_edit_pos, set_edit_pos) def make_readonly(self): self._bpy_selectable = False # This is necessary to prevent the listbox we are in getting # fresh cursor coords of None from get_cursor_coords # immediately after we go readonly and then getting a cached # canvas that still has the cursor set. It spots that # inconsistency and raises. self._invalidate() def set_edit_markup(self, markup): """Call this when markup changes but the underlying text does not. You should arrange for this to be called from the 'change' signal. """ if markup: self._bpy_text, self._bpy_attr = urwid.decompose_tagmarkup(markup) else: # decompose_tagmarkup in some urwids fails on the empty list self._bpy_text, self._bpy_attr = '', [] # This is redundant when we're called off the 'change' signal. # I'm assuming this is cheap, making that ok. self._invalidate() def get_text(self): return self._caption + self._bpy_text, self._attrib + self._bpy_attr def selectable(self): return self._bpy_selectable def get_cursor_coords(self, *args, **kwargs): # urwid gets confused if a nonselectable widget has a cursor position. if not self._bpy_selectable: return None return urwid.Edit.get_cursor_coords(self, *args, **kwargs) def render(self, size, focus=False): # XXX I do not want to have to do this, but listbox gets confused # if I do not (getting None out of get_cursor_coords because # we just became unselectable, then having this render a cursor) if not self._bpy_selectable: focus = False return urwid.Edit.render(self, size, focus=focus) def get_pref_col(self, size): # Need to make this deal with us being nonselectable if not self._bpy_selectable: return 'left' return urwid.Edit.get_pref_col(self, size) def move_cursor_to_coords(self, *args): if self._bpy_may_move_cursor: return urwid.Edit.move_cursor_to_coords(self, *args) return False def keypress(self, size, key): if urwid.command_map[key] in ['cursor up', 'cursor down']: # Do not handle up/down arrow, leave them for the repl. return key self._bpy_may_move_cursor = True try: if urwid.command_map[key] == 'cursor max left': self.edit_pos = 0 elif urwid.command_map[key] == 'cursor max right': self.edit_pos = len(self.get_edit_text()) elif urwid.command_map[key] == 'clear word': # ^w if self.edit_pos == 0: return line = self.get_edit_text() # delete any space left of the cursor p = len(line[:self.edit_pos].strip()) line = line[:p] + line[self.edit_pos:] # delete a full word np = line.rfind(' ', 0, p) if np == -1: line = line[p:] np = 0 else: line = line[:np] + line[p:] self.set_edit_text(line) self.edit_pos = np elif urwid.command_map[key] == 'clear line': line = self.get_edit_text() self.set_edit_text(line[self.edit_pos:]) self.edit_pos = 0 elif key == 'backspace': line = self.get_edit_text() cpos = len(line) - self.edit_pos if not (cpos or len(line) % self.tab_length or line.strip()): self.set_edit_text(line[:-self.tab_length]) else: return urwid.Edit.keypress(self, size, key) else: # TODO: Add in specific keypress fetching code here return urwid.Edit.keypress(self, size, key) return None finally: self._bpy_may_move_cursor = False def mouse_event(self, *args): self._bpy_may_move_cursor = True try: return urwid.Edit.mouse_event(self, *args) finally: self._bpy_may_move_cursor = False class BPythonListBox(urwid.ListBox): """Like `urwid.ListBox`, except that it does not eat up and down keys. """ def keypress(self, size, key): if key not in ["up", "down"]: return urwid.ListBox.keypress(self, size, key) return key class Tooltip(urwid.BoxWidget): """Container inspired by Overlay to position our tooltip. bottom_w should be a BoxWidget. The top window currently has to be a listbox to support shrinkwrapping. This passes keyboard events to the bottom instead of the top window. It also positions the top window relative to the cursor position from the bottom window and hides it if there is no cursor. """ def __init__(self, bottom_w, listbox): self.__super.__init__() self.bottom_w = bottom_w self.listbox = listbox # TODO: this linebox should use the 'main' color. self.top_w = urwid.LineBox(listbox) self.tooltip_focus = False def selectable(self): return self.bottom_w.selectable() def keypress(self, size, key): return self.bottom_w.keypress(size, key) def mouse_event(self, size, event, button, col, row, focus): # TODO: pass to top widget if visible and inside it. if not hasattr(self.bottom_w, 'mouse_event'): return False return self.bottom_w.mouse_event( size, event, button, col, row, focus) def get_cursor_coords(self, size): return self.bottom_w.get_cursor_coords(size) def render(self, size, focus=False): maxcol, maxrow = size bottom_c = self.bottom_w.render(size, focus) cursor = bottom_c.cursor if not cursor: # Hide the tooltip if there is no cursor. return bottom_c cursor_x, cursor_y = cursor if cursor_y * 2 < maxrow: # Cursor is in the top half. Tooltip goes below it: y = cursor_y + 1 rows = maxrow - y else: # Cursor is in the bottom half. Tooltip fills the area above: y = 0 rows = cursor_y # HACK: shrink-wrap the tooltip. This is ugly in multiple ways: # - It only works on a listbox. # - It assumes the wrapping LineBox eats one char on each edge. # - It is a loop. # (ideally it would check how much free space there is, # instead of repeatedly trying smaller sizes) while 'bottom' in self.listbox.ends_visible((maxcol - 2, rows - 3)): rows -= 1 # If we're displaying above the cursor move the top edge down: if not y: y = cursor_y - rows # Render *both* windows focused. This is probably not normal in urwid, # but it works nicely. top_c = self.top_w.render((maxcol, rows), focus and self.tooltip_focus) combi_c = urwid.CanvasOverlay(top_c, bottom_c, 0, y) # Use the cursor coordinates from the bottom canvas. canvas = urwid.CompositeCanvas(combi_c) canvas.cursor = cursor return canvas class URWIDInteraction(repl.Interaction): def __init__(self, config, statusbar, frame): repl.Interaction.__init__(self, config, statusbar) self.frame = frame urwid.connect_signal(statusbar, 'prompt_result', self._prompt_result) self.callback = None def confirm(self, q, callback): """Ask for yes or no and call callback to return the result""" def callback_wrapper(result): callback(result.lower() in (_('y'), _('yes'))) self.prompt(q, callback_wrapper, single=True) def notify(self, s, n=10): return self.statusbar.message(s, n) def prompt(self, s, callback=None, single=False): """Prompt the user for input. The result will be returned via calling callback. Note that there can only be one prompt active. But the callback can already start a new prompt.""" if self.callback is not None: raise Exception('Prompt already in progress') self.callback = callback self.statusbar.prompt(s, single=single) self.frame.set_focus('footer') def _prompt_result(self, text): self.frame.set_focus('body') if self.callback is not None: # The callback might want to start another prompt, so reset it # before calling the callback. callback = self.callback self.callback = None callback(text) class URWIDRepl(repl.Repl): _time_between_redraws = .05 # seconds def __init__(self, event_loop, palette, interpreter, config): repl.Repl.__init__(self, interpreter, config) self._redraw_handle = None self._redraw_pending = False self._redraw_time = 0 self.listbox = BPythonListBox(urwid.SimpleListWalker([])) self.tooltip = urwid.ListBox(urwid.SimpleListWalker([])) self.tooltip.grid = None self.overlay = Tooltip(self.listbox, self.tooltip) self.stdout_hist = '' self.frame = urwid.Frame(self.overlay) if urwid.get_encoding_mode() == 'narrow': input_filter = decoding_input_filter else: input_filter = None # This constructs a raw_display.Screen, which nabs sys.stdin/out. self.main_loop = urwid.MainLoop( self.frame, palette, event_loop=event_loop, unhandled_input=self.handle_input, input_filter=input_filter, handle_mouse=False) # String is straight from bpython.cli self.statusbar = Statusbar(config, _(" <%s> Rewind <%s> Save <%s> Pastebin " " <%s> Pager <%s> Show Source ") % (config.undo_key, config.save_key, config.pastebin_key, config.last_output_key, config.show_source_key), self.main_loop) self.frame.set_footer(self.statusbar.widget) self.interact = URWIDInteraction(self.config, self.statusbar, self.frame) self.edits = [] self.edit = None self.current_output = None self._completion_update_suppressed = False # Bulletproof: this is a value extract_exit_value accepts. self.exit_value = () load_urwid_command_map(config) # Subclasses of Repl need to implement echo, current_line, cw def echo(self, orig_s): s = orig_s.rstrip('\n') if s: if self.current_output is None: self.current_output = urwid.Text(('output', s)) if self.edit is None: self.listbox.body.append(self.current_output) # Focus the widget we just added to force the # listbox to scroll. This causes output to scroll # if the user runs a blocking call that prints # more than a screenful, instead of staying # scrolled to the previous input line and then # jumping to the bottom when done. self.listbox.set_focus(len(self.listbox.body) - 1) else: self.listbox.body.insert(-1, self.current_output) # The edit widget should be focused and *stay* focused. # XXX TODO: make sure the cursor stays in the same spot. self.listbox.set_focus(len(self.listbox.body) - 1) else: # XXX this assumes this all has "output" markup applied. self.current_output.set_text( ('output', self.current_output.text + s)) if orig_s.endswith('\n'): self.current_output = None # If we hit this repeatedly in a loop the redraw is rather # slow (testcase: pprint(__builtins__). So if we have recently # drawn the screen already schedule a call in the future. # # Unfortunately we may hit this function repeatedly through a # blocking call triggered by the user, in which case our # timeout will not run timely as we do not return to urwid's # eventloop. So we manually check if our timeout has long # since expired, and redraw synchronously if it has. if self._redraw_handle is None: self.main_loop.draw_screen() def maybe_redraw(loop, self): if self._redraw_pending: loop.draw_screen() self._redraw_pending = False self._redraw_handle = None self._redraw_handle = self.main_loop.set_alarm_in( self._time_between_redraws, maybe_redraw, self) self._redraw_time = time.time() else: self._redraw_pending = True now = time.time() if now - self._redraw_time > 2 * self._time_between_redraws: # The timeout is well past expired, assume we're # blocked and redraw synchronously. self.main_loop.draw_screen() self._redraw_time = now def current_line(self): """Return the current line (the one the cursor is in).""" if self.edit is None: return '' return self.edit.get_edit_text() def cw(self): """Return the current word (incomplete word left of cursor).""" if self.edit is None: return pos = self.edit.edit_pos text = self.edit.get_edit_text() if pos != len(text): # Disable autocomplete if not at end of line, like cli does. return # Stolen from cli. TODO: clean up and split out. if (not text or (not text[-1].isalnum() and text[-1] not in ('.', '_'))): return # Seek backwards in text for the first non-identifier char: for i, c in enumerate(reversed(text)): if not c.isalnum() and c not in ('.', '_'): break else: # No non-identifiers, return everything. return text # Return everything to the right of the non-identifier. return text[-i:] @property def cpos(self): if self.edit is not None: return len(self.current_line()) - self.edit.edit_pos return 0 def _populate_completion(self): widget_list = self.tooltip.body while widget_list: widget_list.pop() # This is just me flailing around wildly. TODO: actually write. if self.complete(): if self.argspec: # This is mostly just stolen from the cli module. func_name, args, is_bound, in_arg = self.argspec args, varargs, varkw, defaults = args[:4] if py3: kwonly, kwonly_defaults = args[4:] else: kwonly, kwonly_defaults = [], {} markup = [('bold name', func_name), ('name', ': (')] # the isinstance checks if we're in a positional arg # (instead of a keyword arg), I think if is_bound and isinstance(in_arg, int): in_arg += 1 # bpython.cli checks if this goes off the edge and # does clever wrapping. I do not (yet). for k, i in enumerate(args): if defaults and k + 1 > len(args) - len(defaults): kw = repr(defaults[k - (len(args) - len(defaults))]) else: kw = None if not k and str(i) == 'self': color = 'name' else: color = 'token' if k == in_arg or i == in_arg: color = 'bold ' + color if not py3: # See issue #138: We need to format tuple unpacking correctly # We use the undocumented function inspection.strseq() for # that. Fortunately, that madness is gone in Python 3. markup.append((color, inspect.strseq(i, str))) else: markup.append((color, str(i))) if kw is not None: markup.extend([('punctuation', '='), ('token', kw)]) if k != len(args) - 1: markup.append(('punctuation', ', ')) if varargs: if args: markup.append(('punctuation', ', ')) markup.append(('token', '*' + varargs)) if kwonly: if not varargs: if args: markup.append(('punctuation', ', ')) markup.append(('punctuation', '*')) for arg in kwonly: if arg == in_arg: color = 'bold token' else: color = 'token' markup.extend([('punctuation', ', '), (color, arg)]) if arg in kwonly_defaults: markup.extend([('punctuation', '='), ('token', kwonly_defaults[arg])]) if varkw: if args or varargs or kwonly: markup.append(('punctuation', ', ')) markup.append(('token', '**' + varkw)) markup.append(('punctuation', ')')) widget_list.append(urwid.Text(markup)) if self.matches: attr_map = {} focus_map = {'main': 'operator'} texts = [urwid.AttrMap(urwid.Text(('main', match)), attr_map, focus_map) for match in self.matches] width = max(text.original_widget.pack()[0] for text in texts) gridflow = urwid.GridFlow(texts, width, 1, 0, 'left') widget_list.append(gridflow) self.tooltip.grid = gridflow self.overlay.tooltip_focus = False else: self.tooltip.grid = None self.frame.body = self.overlay else: self.frame.body = self.listbox self.tooltip.grid = None if self.docstring: # TODO: use self.format_docstring? needs a width/height... docstring = self.docstring widget_list.append(urwid.Text(('comment', docstring))) def reprint_line(self, lineno, tokens): edit = self.edits[-len(self.buffer) + lineno - 1] edit.set_edit_markup(list(format_tokens(tokens))) def getstdout(self): """This method returns the 'spoofed' stdout buffer, for writing to a file or sending to a pastebin or whatever.""" return self.stdout_hist + '\n' def ask_confirmation(self, q): """Ask for yes or no and return boolean""" try: reply = self.statusbar.prompt(q) except ValueError: return False return reply.lower() in ('y', 'yes') def reevaluate(self): """Clear the buffer, redraw the screen and re-evaluate the history""" self.evaluating = True self.stdout_hist = '' self.f_string = '' self.buffer = [] self.scr.erase() self.s_hist = [] # Set cursor position to -1 to prevent paren matching self.cpos = -1 self.prompt(False) self.iy, self.ix = self.scr.getyx() for line in self.history: if py3: self.stdout_hist += line + '\n' else: self.stdout_hist += line.encode(locale.getpreferredencoding()) + '\n' self.print_line(line) self.s_hist[-1] += self.f_string # I decided it was easier to just do this manually # than to make the print_line and history stuff more flexible. self.scr.addstr('\n') more = self.push(line) self.prompt(more) self.iy, self.ix = self.scr.getyx() self.cpos = 0 indent = repl.next_indentation(self.s, self.config.tab_length) self.s = '' self.scr.refresh() if self.buffer: for _ in xrange(indent): self.tab() self.evaluating = False #map(self.push, self.history) #^-- That's how simple this method was at first :( def write(self, s): """For overriding stdout defaults""" if '\x04' in s: for block in s.split('\x04'): self.write(block) return if s.rstrip() and '\x03' in s: t = s.split('\x03')[1] else: t = s if not py3 and isinstance(t, unicode): t = t.encode(locale.getpreferredencoding()) if not self.stdout_hist: self.stdout_hist = t else: self.stdout_hist += t self.echo(s) self.s_hist.append(s.rstrip()) def push(self, s, insert_into_history=True): # Restore the original SIGINT handler. This is needed to be able # to break out of infinite loops. If the interpreter itself # sees this it prints 'KeyboardInterrupt' and returns (good). orig_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, signal.default_int_handler) # Pretty blindly adapted from bpython.cli try: return repl.Repl.push(self, s, insert_into_history) except SystemExit, e: self.exit_value = e.args raise urwid.ExitMainLoop() except KeyboardInterrupt: # KeyboardInterrupt happened between the except block around # user code execution and this code. This should be rare, # but make sure to not kill bpython here, so leaning on # ctrl+c to kill buggy code running inside bpython is safe. self.keyboard_interrupt() finally: signal.signal(signal.SIGINT, orig_handler) def start(self): self.prompt(False) def keyboard_interrupt(self): # If the user is currently editing, interrupt him. This # mirrors what the regular python REPL does. if self.edit is not None: # XXX this is a lot of code, and I am not sure it is # actually enough code. Needs some testing. self.edit.make_readonly() self.edit = None self.buffer = [] self.echo('KeyboardInterrupt') self.prompt(False) else: # I do not quite remember if this is reachable, but let's # be safe. self.echo('KeyboardInterrupt') def prompt(self, more): # Clear current output here, or output resulting from the # current prompt run will end up appended to the edit widget # sitting above this prompt: self.current_output = None # XXX is this the right place? self.rl_history.reset() # XXX what is s_hist? # We need the caption to use unicode as urwid normalizes later # input to be the same type, using ascii as encoding. If the # caption is bytes this breaks typing non-ascii into bpython. # Currently this decodes using ascii as I do not know where # ps1 is getting loaded from. If anyone wants to make # non-ascii prompts work feel free to fix this. if not more: caption = ('prompt', self.ps1.decode('ascii')) self.stdout_hist += self.ps1 else: caption = ('prompt_more', self.ps2.decode('ascii')) self.stdout_hist += self.ps2 self.edit = BPythonEdit(self.config, caption=caption) urwid.connect_signal(self.edit, 'change', self.on_input_change) urwid.connect_signal(self.edit, 'edit-pos-changed', self.on_edit_pos_changed) # Do this after connecting the change signal handler: self.edit.insert_text(4 * self.next_indentation() * ' ') self.edits.append(self.edit) self.listbox.body.append(self.edit) self.listbox.set_focus(len(self.listbox.body) - 1) # Hide the tooltip self.frame.body = self.listbox def on_input_change(self, edit, text): # TODO: we get very confused here if "text" contains newlines, # so we cannot put our edit widget in multiline mode yet. # That is probably fixable... tokens = self.tokenize(text, False) edit.set_edit_markup(list(format_tokens(tokens))) if not self._completion_update_suppressed: # If we call this synchronously the get_edit_text() in repl.cw # still returns the old text... self.main_loop.set_alarm_in( 0, lambda *args: self._populate_completion()) def on_edit_pos_changed(self, edit, position): """Gets called when the cursor position inside the edit changed. Rehighlight the current line because there might be a paren under the cursor now.""" tokens = self.tokenize(self.current_line(), False) edit.set_edit_markup(list(format_tokens(tokens))) def handle_input(self, event): # Since most of the input handling here should be handled in the edit # instead, we return here early if the edit doesn't have the focus. if self.frame.get_focus() != 'body': return if event == 'enter': inp = self.edit.get_edit_text() self.history.append(inp) self.edit.make_readonly() # XXX what is this s_hist thing? self.stdout_hist += inp.encode(locale.getpreferredencoding()) + '\n' self.edit = None # This may take a while, so force a redraw first: self.main_loop.draw_screen() more = self.push(inp) self.prompt(more) elif event == 'ctrl d': # ctrl+d on an empty line exits, otherwise deletes if self.edit is not None: if not self.edit.get_edit_text(): raise urwid.ExitMainLoop() else: self.main_loop.process_input(['delete']) elif urwid.command_map[event] == 'cursor up': # "back" from bpython.cli self.rl_history.enter(self.edit.get_edit_text()) self.edit.set_edit_text('') self.edit.insert_text(self.rl_history.back()) elif urwid.command_map[event] == 'cursor down': # "fwd" from bpython.cli self.rl_history.enter(self.edit.get_edit_text()) self.edit.set_edit_text('') self.edit.insert_text(self.rl_history.forward()) elif urwid.command_map[event] == 'next selectable': self.tab() elif urwid.command_map[event] == 'prev selectable': self.tab(True) #else: # self.echo(repr(event)) def tab(self, back=False): """Process the tab key being hit. If the line is blank or has only whitespace: indent. If there is text before the cursor: cycle completions. If `back` is True cycle backwards through completions, and return instead of indenting. Returns True if the key was handled. """ self._completion_update_suppressed = True try: # Heavily inspired by cli's tab. text = self.edit.get_edit_text() if not text.lstrip() and not back: x_pos = len(text) - self.cpos num_spaces = x_pos % self.config.tab_length if not num_spaces: num_spaces = self.config.tab_length self.edit.insert_text(' ' * num_spaces) return True if not self.matches_iter: self.complete(tab=True) cw = self.current_string() or self.cw() if not cw: return True else: cw = self.matches_iter.current_word b = os.path.commonprefix(self.matches) if b: insert = b[len(cw):] self.edit.insert_text(insert) expanded = bool(insert) if expanded: self.matches_iter.update(b, self.matches) else: expanded = False if not expanded and self.matches: if self.matches_iter: self.edit.set_edit_text( text[:-len(self.matches_iter.current())] + cw) if back: current_match = self.matches_iter.previous() else: current_match = self.matches_iter.next() if current_match: self.overlay.tooltip_focus = True if self.tooltip.grid: self.tooltip.grid.set_focus(self.matches_iter.index) self.edit.insert_text(current_match[len(cw):]) return True finally: self._completion_update_suppressed = False def main(args=None, locals_=None, banner=None): translations.init() # TODO: maybe support displays other than raw_display? config, options, exec_args = bpargs.parse(args, ( 'Urwid options', None, [ Option('--twisted', '-T', action='store_true', help=_('Run twisted reactor.')), Option('--reactor', '-r', help=_('Select specific reactor (see --help-reactors). ' 'Implies --twisted.')), Option('--help-reactors', action='store_true', help=_('List available reactors for -r.')), Option('--plugin', '-p', help=_('twistd plugin to run (use twistd for a list). ' 'Use "--" to pass further options to the plugin.')), Option('--server', '-s', type='int', help=_('Port to run an eval server on (forces Twisted).')), ])) if options.help_reactors: try: from twisted.application import reactors # Stolen from twisted.application.app (twistd). for r in reactors.getReactorTypes(): print ' %-4s\t%s' % (r.shortName, r.description) except ImportError: sys.stderr.write('No reactors are available. Please install ' 'twisted for reactor support.\n') return palette = [ (name, COLORMAP[color.lower()], 'default', 'bold' if color.isupper() else 'default') for name, color in config.color_scheme.iteritems()] palette.extend([ ('bold ' + name, color + ',bold', background, monochrome) for name, color, background, monochrome in palette]) if options.server or options.plugin: options.twisted = True if options.reactor: try: from twisted.application import reactors except ImportError: sys.stderr.write('No reactors are available. Please install ' 'twisted for reactor support.\n') return try: # XXX why does this not just return the reactor it installed? reactor = reactors.installReactor(options.reactor) if reactor is None: from twisted.internet import reactor except reactors.NoSuchReactor: sys.stderr.write('Reactor %s does not exist\n' % ( options.reactor,)) return event_loop = TwistedEventLoop(reactor) elif options.twisted: try: from twisted.internet import reactor except ImportError: sys.stderr.write('No reactors are available. Please install ' 'twisted for reactor support.\n') return event_loop = TwistedEventLoop(reactor) else: # None, not urwid.SelectEventLoop(), to work with # screens that do not support external event loops. event_loop = None # TODO: there is also a glib event loop. Do we want that one? # __main__ construction from bpython.cli if locals_ is None: main_mod = sys.modules['__main__'] = ModuleType('__main__') locals_ = main_mod.__dict__ if options.plugin: try: from twisted import plugin from twisted.application import service except ImportError: sys.stderr.write('No twisted plugins are available. Please install ' 'twisted for twisted plugin support.\n') return for plug in plugin.getPlugins(service.IServiceMaker): if plug.tapname == options.plugin: break else: sys.stderr.write('Plugin %s does not exist\n' % (options.plugin,)) return plugopts = plug.options() plugopts.parseOptions(exec_args) serv = plug.makeService(plugopts) locals_['service'] = serv reactor.callWhenRunning(serv.startService) exec_args = [] interpreter = repl.Interpreter(locals_, locale.getpreferredencoding()) # This nabs sys.stdin/out via urwid.MainLoop myrepl = URWIDRepl(event_loop, palette, interpreter, config) if options.server: factory = EvalFactory(myrepl) reactor.listenTCP(options.server, factory, interface='127.0.0.1') if options.reactor: # Twisted sets a sigInt handler that stops the reactor unless # it sees a different custom signal handler. def sigint(*args): reactor.callFromThread(myrepl.keyboard_interrupt) signal.signal(signal.SIGINT, sigint) # Save stdin, stdout and stderr for later restoration orig_stdin = sys.stdin orig_stdout = sys.stdout orig_stderr = sys.stderr # urwid's screen start() and stop() calls currently hit sys.stdin # directly (via RealTerminal.tty_signal_keys), so start the screen # before swapping sys.std*, and swap them back before restoring # the screen. This also avoids crashes if our redirected sys.std* # are called before we get around to starting the mainloop # (urwid raises an exception if we try to draw to the screen # before starting it). def run_with_screen_before_mainloop(): try: # Currently we just set this to None because I do not # expect code hitting stdin to work. For example: exit() # (not sys.exit, site.py's exit) tries to close sys.stdin, # which breaks urwid's shutdown. bpython.cli sets this to # a fake object that reads input through curses and # returns it. When using twisted I do not think we can do # that because sys.stdin.read and friends block, and we # cannot re-enter the reactor. If using urwid's own # mainloop we *might* be able to do something similar and # re-enter its mainloop. sys.stdin = None #FakeStdin(myrepl) sys.stdout = myrepl sys.stderr = myrepl myrepl.main_loop.set_alarm_in(0, start) while True: try: myrepl.main_loop.run() except KeyboardInterrupt: # HACK: if we run under a twisted mainloop this should # never happen: we have a SIGINT handler set. # If we use the urwid select-based loop we just restart # that loop if interrupted, instead of trying to cook # up an equivalent to reactor.callFromThread (which # is what our Twisted sigint handler does) myrepl.main_loop.set_alarm_in( 0, lambda *args: myrepl.keyboard_interrupt()) continue break finally: sys.stdin = orig_stdin sys.stderr = orig_stderr sys.stdout = orig_stdout # This needs more thought. What needs to happen inside the mainloop? def start(main_loop, user_data): if exec_args: bpargs.exec_code(interpreter, exec_args) if not options.interactive: raise urwid.ExitMainLoop() if not exec_args: sys.path.insert(0, '') # this is CLIRepl.startup inlined. filename = os.environ.get('PYTHONSTARTUP') if filename and os.path.isfile(filename): with open(filename, 'r') as f: if py3: interpreter.runsource(f.read(), filename, 'exec') else: interpreter.runsource(f.read(), filename, 'exec', encode=False) if banner is not None: repl.write(banner) repl.write('\n') myrepl.start() # This bypasses main_loop.set_alarm_in because we must *not* # hit the draw_screen call (it's unnecessary and slow). def run_find_coroutine(): if find_coroutine(): main_loop.event_loop.alarm(0, run_find_coroutine) run_find_coroutine() myrepl.main_loop.screen.run_wrapper(run_with_screen_before_mainloop) if config.flush_output and not options.quiet: sys.stdout.write(myrepl.getstdout()) if hasattr(sys.stdout, "flush"): sys.stdout.flush() return repl.extract_exit_value(myrepl.exit_value) def load_urwid_command_map(config): urwid.command_map[key_dispatch[config.up_one_line_key]] = 'cursor up' urwid.command_map[key_dispatch[config.down_one_line_key]] = 'cursor down' urwid.command_map[key_dispatch['C-a']] = 'cursor max left' urwid.command_map[key_dispatch['C-e']] = 'cursor max right' urwid.command_map[key_dispatch[config.pastebin_key]] = 'pastebin' urwid.command_map[key_dispatch['C-f']] = 'cursor right' urwid.command_map[key_dispatch['C-b']] = 'cursor left' urwid.command_map[key_dispatch['C-d']] = 'delete' urwid.command_map[key_dispatch[config.clear_word_key]] = 'clear word' urwid.command_map[key_dispatch[config.clear_line_key]] = 'clear line' """ 'clear_screen': 'C-l', 'cut_to_buffer': 'C-k', 'down_one_line': 'C-n', 'exit': '', 'last_output': 'F9', 'pastebin': 'F8', 'save': 'C-s', 'show_source': 'F2', 'suspend': 'C-z', 'undo': 'C-r', 'up_one_line': 'C-p', 'yank_from_buffer': 'C-y'}, """ if __name__ == '__main__': sys.exit(main()) bpython-0.12/sample.theme0000644000175000017500000000113112031025543015707 0ustar simonsimon00000000000000# Each letter represents a colour marker: # k, r, g, y, b, m, c, w, d # which stands for: # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default # Capital letters represent bold # Copy to ~/.bpython/foo.theme and set "color_scheme = foo" in ~/.bython/config [syntax] keyword = y name = c comment = b string = m error = r number = G operator = Y paren = y punctuation = y token = C paren = R [interface] # XXX: gnome-terminal appears to be braindead. The cursor will disappear unless # you set the background colour to "d". background = d output = w main = c prompt = c prompt_more = g bpython-0.12/AUTHORS0000644000175000017500000000153712031025543014464 0ustar simonsimon00000000000000bpython is written and maintained by Bob Farrell and Andreas Stuehrk , . Other contributors are (in alphabetical order): * Federico Ceratto * Ingrid Cheung * Martha Girdler * Eike Hein * Allison Kaptur * Jason Laster * Brandon Navra * Michele Orrù * Pavel Panchekha * Sebastian Ramacher * Amjith Ramanujam * Simon de Vlieger * Marien Zwart Many thanks for all contributions! bpython-0.12/CHANGELOG0000644000175000017500000004306512061403133014626 0ustar simonsimon00000000000000Changelog ========= 0.12 ---- We want to give special thanks to the Hacker School project- (https://www.hackerschool.com/) for choosing bpython as their pet hacking project. In special we would like to thank the following people for contributing their code to bpython: - Martha Girdler - Allison Kaptur - Ingrid Cheung We'd also like to thank Eike Hein for contributing his pastebin code which now makes it possible to paste using a 3rd party program unlocking a whole slew of pastebins for bpython users. * Added a new pastebin_helper config option to name an executable that should perform pastebin upload on bpython's behalf. If set, this overrides pastebin_url. Data is supplied to the helper via STDIN, and it is expected to return a pastebin URL as the first word of its output. * Fixed a bug causing pastebin upload to fail after a previous attempt was unsuccessful. A duplicate pastebin error would be displayed in this case, despite the original upload having failed. * Added more key shortcuts to bpython.urwid * Smarter dedenting after certain expressions * #74 fixed broken completion when auto_display_list was disabled We also have done numerous cleanup actions including building the man pages from our documentation. Including the documentation in the source directory. Some minor changes to the README to have EOL 79 and changes to urwid to work better without twisted installed. v0.11 ----- A bugfix/cleanup release .The fixed bugs are: * #204: "import math" not autocompleting on python 3.2 Otherwise lots of small additions to the to be replacement for our ncurses frontend, the urwid frontend. I'd like to specifically thank Amjith Ramanujam for his work on history search which was further implemented and is in working order right now. v0.10.1 ------- A bugfix release. The fixed bugs are: * #197: find_modules crashes on non-readable directories * #198: Source tarball lacks .po files v0.10 ----- As a highlight of the release, Michele Orrù added i18n support to bpython. Some issues have been resolved as well: * Config files are now located according to the XDG Base Directory Specification. The support for the old bpythonrc files has been dropped and ~/.bpython.ini as config file location is no longer supported. See issue #91. * Fixed some issues with tuple unpacking in argspec. See issues #133 and #138. * Fixed a crash with non-ascii filenames in import completion. See issue #139. * Fixed a crash caused by inspect.findsource() raising an IndexError which happens in some situations. See issue #94. * Non-ascii input should work now under Python 3. * Issue #165: C-a and C-e do the right thing now in urwid. * The short command-line option "-c config" was dropped as it conflicts with vanilla Python's "-c command" option. See issue #186. v0.9.7.1 -------- A bugfix release. The fixed bugs are: * #128: bpython-gtk is broken * #134: crash when using pastebin and no active internet connection v0.9.7 ------ Well guys. It's been some time since the latest release, six months have passed We have added a whole slew of new features, and closed a number of bugs as well. We also have a new frontend for bpython. Marien Zwart contributed a urwid frontend as an alternative for the curses frontend. Be aware that there still is a lot to fix for this urwid frontend (a lot of the keyboard shortcuts do not yet work for example) but please give it a good spin. Urwid also optionally integrates with a Twisted reactor and through that with things like the GTK event loop. At the same time we have done a lot of work on the GTK frontend. The GTK frontend is now 'usable'. Please give that a spin as well by running bpython-gtk on you system. We also welcome a new contributor in the name of Michele Orrù who we hope will help us fix even more bugs and improve functionality. As always, please submit any bugs you might find to our bugtracker. * Pastebin confirmation added; we were getting a lot of people accidentally pastebinning sensitive information so I think this is a good idea. * Don't read PYTHONSTARTUP when executed with -i. * BPDB was merged in. BPDB is an extension to PDB which allows you to press B in a PDB session which will let you be dropped into a bpython sessions with the current PDB locals(). For usage, see the documentation. * The clear word shortcut (default: C-w) now deletes to the buffer. * More tests have been added to bpython. * The pastebin now checks for a previous paste (during the session) with the exact same content to guard against twitchy fingers pastebinning multiple times. * Let import completion return "import " instead of "import". * GTK now has pastebin, both for full log as well as the current selection. * GTK now has write2file. * GTK now has a menu. * GTK now has a statusbar. * GTK now has show source functionality. * GTK saves the pastebin url to the clipboard. * GTK now has it's own configuration section. * Set focus to the GTK text widget to allow for easier embedding in PIDA and others which fixes issues #121. * #87: Add a closed attribute to Repl to fix mercurial.ui.ui expecting stderr to have this attribute. * #108: Unicode characters in docsrting crash bpython * #118: Load_theme is not defined. * #99: Configurable font now documented. * #123: Pastebin can't handle 'ESC' key * #124: Unwanted input when using / keys in the statusbar prompt. v0.9.6.2 -------- Unfortunately another bugfix release as I (Bob) broke py3 support. * #84: bpython doesn't work with Python 3 Thanks very much to Henry Prêcheur for both the bug report and the patch. v0.9.6.1 -------- A quick bugfix release (this should not become a habit). * #82: Crash on saving file. v0.9.6 ------ A bugfix/feature release (and a start at gtk). Happy Christmas everyone! * #67: Make pastebin URL really configurable. * #68: Set a__main__ module and set interpreter's namespace to that module. * #70: Implement backward completion on backward tab. * #62: Hide matches starting with a _ unless explicitly typed. * #72: Auto dedentation * #78: Theme without a certain value raises exception - add the possibility for a banner to be shown on bpython startup (when embedded or in code) written by Caio Romao. - add a hack to add a write() method to our fake stdin object - Don't use curses interface when stdout is not attached to a terminal. - PEP-8 conformance. - Only restore indentation when inside a block. - Do not decrease the lineno in tracebacks for Py3 - Do not add internal code to history. - Make paren highlighting more accurate. - Catch SyntaxError in import completion. - Remove globals for configuration. - rl_history now stays the same, also after undo. v0.9.5.2 -------- A bugfix release. Fixed issues: * #60: Filename expansion: Cycling completions and deleting * #61: Filename expansion: Directory names with '.'s get mangled Other fixes without opened issues: * Encode items in the suggestion list properly * Expand usernames in file completion correctly * future imports in startup scripts can influence interpreter's behaviour now * Show the correct docstring for types without a own __init__ method v0.9.5.1 -------- Added missing data files to the tarball. v0.9.5 ------ Fixed issues: * #25 Problems with DEL, Backspace and C-u over multiple lines * #49 Sending last output to $PAGER * #51 Ability to embed bpython shell into an existing script * #52 FakeStdin.readlines() is broken * #53 Error on printing null character * #54 Parsing/introspection ncurses viewer neglects parenthesis bpython has added a view source shortcut to show the source of the current function. The history file is now really configurable. This issue was reported in Debian's bugtracker. bpython has now some basic support for Python 3 (requires Pygments >=1.1.1). As a result, setuptools is now optional. The pastebin URL is now configurable and the default pastebin is now bpaste.net Argument names are now shown as completion suggestions and one can tab through the completion list. v0.9.4 ------ Bugfix release (mostly) * when typing a float literal bpython autocompletes int methods (#36) * Autocompletion for file names (#40) * Indenting doesn't reset (#27) * bpython configuration has moved from ~/.bpython.ini to ~/.bpython/config (currently still supporting fallback) * leftovers of statusbar when exiting bpython cleaned up * bpython now does not crash when a 'popup' goes out of window bounds * numerous fixes and improvements to parentheses highlighting * made *all* keys configurable (except for arrow keys/pgup/pgdown) v0.9.3 ------ This release was a true whopper! * Full unicode support * Configurable hotkey support * Theming support * Pastemode, disables syntax highlighting during a paste for faster pasting, highlights when done * Parentheses matching * Argument highlighting v0.9.2 ------ * help() now uses an external pager if available. * Fix for highlighting prefixed strings. * Fix to reset string highlighting after a SyntaxError. * bpython now uses optparse for option parsing and it supports --version now. * Configuration files are no longer passed by the first command line argument but by the -c command line switch. * Fix for problem related to editing lines in the history: http://bitbucket.org/bobf/bpython/issue/10/odd-behaviour-when-editing-commands-in-the-history v0.9.1 ------ * Fixed a small but annoying bug with sys.argv ini file passing * Fix for Python 2.6 to monkeypatch they way it detects callables in rlcompleter * Config file conversion fix v0.9.0 ------ * Module import completion added. * Changed to paste.pocoo.org due to rafb.net no longer offering a pastebin service. * Switched to .ini file format for config file. * White background-friendly colour scheme added. * C-l now clears the screen. * SyntaxError now correctly added to history to prevent it garbling up on a redraw. Probably some other things, but I hate changelogs. :) v0.8.0 ------ It's been a long while since the last release and there've been numerous little bugfixes and extras here and there so I'm putting this out as 0.8.0. Check the hg commit history if you want more info: http://bitbucket.org/bobf/bpython/ v0.7.2 ------ Menno sent me some patches to fix some stuff: * Socket error handled when submitting to a pastebin. * Resizing could crash if you resize small enough. Other stuff: * 'self' in arg list is now highlighted a different colour. * flush_output option added to config to control whether output is flushed to stdout or not on exit. * Piping something to bpython made it lock up as stdin was not the keyboard - bpython just executes stdin and exits instead of trying to do something clever. * Mark Florisson (eggy) gave me a patch that stops weird breakage when unicode objects get added into the output buffer - they now get encoded into the output encoding. * Bohdan Vlasyuk sent me a patch that fixes a problem with the above patch from Mark if sys.__stdout__.encoding didn't exist. * Save to file now outputs executable code (i.e. without the >>> and ... and with "# OUT: " prepended to all output lines). I never used this feature much but someone asked for this behaviour. v0.7.1 ------ * Added support for a history file, defaults to ~/.pythonhist and 100 lines but is configurable from the rc file (see sample-rc). * Charles Duffy has added a yank/put thing - C-k and C-y. He also ran the code through some PEP-8 checker thing and fixed up a few old habits I manage to break but didn't manage to fix the code to reflect this - thank you! * Jørgen Tjernø has fixed up the autoindentation issues we encountered when bringing soft tabs in. * SyntaxError, ValueError and OverflowError are now caught properly (code.InteractiveInterpreter treats these as different to other exceptions as it doesn't print the whole traceback, so a different handler is called). This was discovered as I was trying to stop autoindentation from occurring on a SyntaxError, which has also been fixed. * '.' now in sys.path on startup. v0.7.0 ------ C-d behaviour changed so it no longer exits if the current line isn't empty. Extra linebreak added to end of stdout flush. pygments and pyparsing are now dependencies. Jørgen Tjernø has done lots of cool things like write a manpage and .desktop file and improved the way tabbing works and also added home, end and del key handling as well as C-w for deleting words - thanks a lot! raw_input() and all its friends now work fine. PYTHONSTARTUP handled without blowing up on stupid errors (it now parses the file at once instead of feeding it to the repl line-by-line). v0.6.4 ------ KeyboardInterrupt handler clears the list window properly now. v0.6.3 ------ Forgot to switch rpartition to split for 2.4 compat. v0.6.2 ------ The help() now works (as far as I can see) exactly the same as the vanilla help() in the regular interpreter. I copied some code from pydoc.py to make it handle the special cases, e.g. help('keywords') help('modules') etc. v0.6.1 ------ Somehow it escaped my attention that the list window was never fully using the rightmost column, except for the first row. This is because me and numbers don't have the best relationship. I think stability is really improving with the latest spat of bugfixes, keep me informed of any bugs. v0.6.0 ------ No noticeable changes except that bpython should now work with Python 2.4. Personally I think it's silly to make a development tool work with an out of date version of Python but some people seem to disagree. The only real downside is that I had to do a horrible version of all() using reduce(), otherwise there's no real differences in the code. v0.5.3 ------ Now you can configure a ~/.bpythonrc file (or pass a rc file at the command line (bpython /foo/bar). See README for details. v0.5.2 ------ help() actually displays the full help page, and I fixed up the ghetto pager a little. v0.5.1 ------ Now you can hit tab to display the autocomplete list, rather than have it pop up automatically as you type which, apparently, annoys Brendogg. v0.5.0 ------ A few people have commented that the help() built-in function doesn't work so well with bpython, since Python will try to output the help string to PAGER (usually "less") which obviously makes everything go wrong when curses is involved. With a bit of hackery I've written my own ghetto pager and injected my own help function into the interpreter when it initialises in an attempt to rectify this. As such, it's pretty untested but it seems to be working okay for me. Suggestions/bug reports/patches are welcome regarding this. v0.4.2 ------ Well, hopefully we're one step closer to making the list sizing stuff work. I really hate doing code for that kind of thing as I never get it quite right, but with perseverence it should end up being completely stable; it's not the hardest thing in the world. Various cosmetic fixes have been put in at the request of a bunch of people who were kind enough to send me emails regarding their experiences. PYTHONSTARTUP is now dealt with and used properly, as per the vanilla interpreter. v0.4.1 ------ It looks like the last release was actually pretty bug-free, aside from one tiny bug that NEVER ACTUALLY HAPPENS but someone was bugging me about it anyway, oh well. v0.4.0 ------ It's been quite a long time since the last update, due to several uninteresting and invalid excuses, but I finally reworked the list drawing procedures so the crashing seems to have been taken care of to an extent. If it still crashes, the way I've written it will hopefully allow a much more robust way of fixing it, one that might actually work. v0.3.2 ------ Thanks to Aaron Gallagher for pointing out a case where the hugely inefficient list generation routines were actually making a significant issue; they're much more efficient now and should hopefully not cause any more problems. v0.3.1 ------ Thanks to Klaus Alexander Seis for the expanduser() patch. Auto indent works on multiple levels now. v0.3.0 ------ Now with auto-indent. Let me know if it's annoying. v0.2.4 ------ Thanks a lot to Angus Gibson for submitting a patch to fix a problem I was having with initialising the keyboard stuff in curses properly. Also a big thanks to John Beisley for providing the patch that shows a class __init__ method's argspec on class instantiation. I've fixed up the argspec display so it handles really long argspecs (e.g. subprocess.Popen()) and doesn't crash if something horrible happens (rather, it avoids letting something horrible happen). I decided to add a key that will get rid of the autocomplete window, since it can get in the way. C-l seemed like a good choice, since it would work well as a side-effect of redrawing the screen (at least that makes sense to me). In so doing I also cleaned up a lot of the reevaluating and resizing code so that a lot of the strange output seen on Rewind/resize seems to be gone. v0.2.3 ------ The fix for the last bug broke the positioning of the autocomplete box, whoops. v0.2.2 ------ That pesky bug keeps coming up. I think it's finally nailed but it's just a matter of testing and hoping. I hate numbers. v0.2.1 ------ I'm having a bit of trouble with some integer division that's causing trouble when a certain set of circumstances arise, and I think I've taken care of that little bug, since it's a real pain in the ass and only creeps up when I'm actually doing something useful, so I'll test it for a bit and release it as hopefully a bug fixed version. v0.2.0 ------ A little late in the day to start a changelog, but here goes... This version fixed another annoying little bug that was causing crashes given certain exact circumstances. I always find it's the way with curses and sizing of windows and things... I've also got bpython to try looking into pydoc if no matches are found for the argspec, which means the builtins have argspecs too now, hooray. bpython-0.12/TODO0000644000175000017500000000014412031025543014075 0ustar simonsimon00000000000000bpython.gtk_ - make keys work bpython.urwid - make keys work bpython.cli bpython.qt - write this bpython-0.12/.pycheckrc0000644000175000017500000000006412031025543015362 0ustar simonsimon00000000000000blacklist = ['pyparsing', 'code', 'pygments/lexer'] bpython-0.12/MANIFEST.in0000644000175000017500000000076412061377417015170 0ustar simonsimon00000000000000include .pycheckrc include AUTHORS include CHANGELOG include LICENSE include data/bpython include data/bpython-gtk include data/bpython.desktop include doc/sphinx/source/conf.py include doc/sphinx/source/*.rst include doc/sphinx/source/logo.png include sample-config include *.theme include bpython/logo.png include ROADMAP include TODO include bpython/test/*.py include bpython/test/*.theme include bpython/translations/*/LC_MESSAGES/bpython.po include bpython/translations/*/LC_MESSAGES/bpython.mo bpython-0.12/data/0000755000175000017500000000000012061403162014317 5ustar simonsimon00000000000000bpython-0.12/data/bpython0000755000175000017500000000012112031025543015722 0ustar simonsimon00000000000000#!/usr/bin/env python import sys from bpython.cli import main sys.exit(main()) bpython-0.12/data/bpython-gtk0000755000175000017500000000012212031025543016506 0ustar simonsimon00000000000000#!/usr/bin/env python import sys from bpython.gtk_ import main sys.exit(main()) bpython-0.12/data/bpython.desktop0000644000175000017500000000035112031025543017374 0ustar simonsimon00000000000000[Desktop Entry] Icon=/usr/share/pixmaps/python.xpm Name=bpython Comment=A fancy interface to the python interpreter! Exec=/usr/bin/bpython Terminal=true Type=Application Categories=Development;Utility;ConsoleOnly; StartupNotify=true bpython-0.12/bpdb/0000755000175000017500000000000012061403162014315 5ustar simonsimon00000000000000bpython-0.12/bpdb/debugger.py0000644000175000017500000000357312031025543016463 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Bob Farrell # # 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. import pdb import bpython class BPdb(pdb.Pdb): """ PDB with BPython support. """ def __init__(self): pdb.Pdb.__init__(self) self.rcLines = [] self.prompt = '(BPdb) ' self.intro = 'Use "B" to enter bpython, Ctrl-d to exit it.' def postloop(self): # We only want to show the intro message once. self.intro = None pdb.Pdb.postloop(self) ### cmd.Cmd commands def do_Bpython(self, arg): bpython.embed(self.curframe.f_locals, ['-i']) def help_Bpython(self): print "B(python)" print print ("Invoke the bpython interpreter for this stack frame. To exit " "bpython and return to a standard pdb press Ctrl-d") ### shortcuts do_B = do_Bpython help_B = help_Bpython bpython-0.12/bpdb/__init__.py0000644000175000017500000000364512031025543016436 0ustar simonsimon00000000000000# The MIT License # # Copyright (c) 2008 Bob Farrell # # 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. import sys import bpython from bpdb.debugger import BPdb __version__ = bpython.__version__ def set_trace(): """ Just like pdb.set_trace(), a helper function that creates a debugger instance and sets the trace. """ debugger = BPdb() debugger.set_trace(sys._getframe().f_back) # Adopted verbatim from pdb for completeness: def post_mortem(t=None): # handling the default if t is None: # sys.exc_info() returns (type, value, traceback) if an exception is # being handled, otherwise it returns None t = sys.exc_info()[2] if t is None: raise ValueError("A valid traceback must be passed if no " "exception is being handled") p = BPdb() p.reset() p.interaction(None, t) def pm(): post_mortem(getattr(sys, "last_traceback", None)) bpython-0.12/bpython.egg-info/0000755000175000017500000000000012061403162016563 5ustar simonsimon00000000000000bpython-0.12/bpython.egg-info/entry_points.txt0000644000175000017500000000020012061403162022051 0ustar simonsimon00000000000000[console_scripts] bpython-urwid = bpython.urwid:main bpython = bpython.cli:main [gui_scripts] bpython-gtk = bpython.gtk_:main bpython-0.12/bpython.egg-info/dependency_links.txt0000644000175000017500000000000112061403162022631 0ustar simonsimon00000000000000 bpython-0.12/bpython.egg-info/top_level.txt0000644000175000017500000000001512061403162021311 0ustar simonsimon00000000000000bpdb bpython bpython-0.12/bpython.egg-info/requires.txt0000644000175000017500000000001012061403162021152 0ustar simonsimon00000000000000pygmentsbpython-0.12/bpython.egg-info/PKG-INFO0000644000175000017500000000057712061403162017671 0ustar simonsimon00000000000000Metadata-Version: 1.0 Name: bpython Version: 0.12 Summary: Fancy Interface to the Python Interpreter Home-page: http://www.bpython-interpreter.org/ Author: Bob Farrell, Andreas Stuehrk et al. Author-email: robertanthonyfarrell@gmail.com License: MIT/X Description: bpython is a fancy interface to the Python interpreter for Unix-like operating systems. Platform: UNKNOWN bpython-0.12/bpython.egg-info/SOURCES.txt0000644000175000017500000000354512061403162020456 0ustar simonsimon00000000000000.pycheckrc AUTHORS CHANGELOG LICENSE MANIFEST.in ROADMAP TODO light.theme sample-config sample.theme setup.py windows.theme bpdb/__init__.py bpdb/debugger.py bpython/__init__.py bpython/_internal.py bpython/_py3compat.py bpython/args.py bpython/autocomplete.py bpython/cli.py bpython/config.py bpython/formatter.py bpython/gtk_.py bpython/importcompletion.py bpython/inspection.py bpython/keys.py bpython/logo.png bpython/pager.py bpython/repl.py bpython/urwid.py bpython.egg-info/PKG-INFO bpython.egg-info/SOURCES.txt bpython.egg-info/dependency_links.txt bpython.egg-info/entry_points.txt bpython.egg-info/requires.txt bpython.egg-info/top_level.txt bpython/test/__init__.py bpython/test/test.theme bpython/test/test_args.py bpython/test/test_bpython.py bpython/test/test_config.py bpython/test/test_crashers.py bpython/test/test_formatter.py bpython/test/test_gtk_.py bpython/test/test_importcompletion.py bpython/test/test_inspection.py bpython/test/test_keys.py bpython/test/test_pager.py bpython/test/test_repl.py bpython/test/test_wizard.py bpython/translations/__init__.py bpython/translations/de/LC_MESSAGES/bpython.po bpython/translations/es_ES/LC_MESSAGES/bpython.po bpython/translations/it_IT/LC_MESSAGES/bpython.po bpython/translations/nl_NL/LC_MESSAGES/bpython.po data/bpython data/bpython-gtk data/bpython.desktop doc/sphinx/source/authors.rst doc/sphinx/source/bpaste.rst doc/sphinx/source/bpdb.rst doc/sphinx/source/changelog.rst doc/sphinx/source/community.rst doc/sphinx/source/conf.py doc/sphinx/source/configuration-options.rst doc/sphinx/source/configuration.rst doc/sphinx/source/django.rst doc/sphinx/source/index.rst doc/sphinx/source/logo.png doc/sphinx/source/man-bpython-config.rst doc/sphinx/source/man-bpython.rst doc/sphinx/source/releases.rst doc/sphinx/source/sourcecode.rst doc/sphinx/source/themes.rst doc/sphinx/source/tips.rst doc/sphinx/source/windows.rstbpython-0.12/windows.theme0000644000175000017500000000122212031025543016121 0ustar simonsimon00000000000000# Each letter represents a colour marker: # k, r, g, y, b, m, c, w, d # which stands for: # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default # # But in Windows (for now) # K, R, G, Y, B, M, C, W, k # blacK, blue, Green, cyan, red, Magenta, yellow, hite, grey # Capital letters represent bold (brighter) # Copy to %USERPROFILE%\.bpython\windows.theme and set "color_scheme = windows" in %USERPROFILE%\.bpython\config [syntax] keyword = G name = Y comment = c string = M error = B number = C operator = G punctuation = C token = M [interface] background = r output = w main = W prompt = W prompt_more = K bpython-0.12/PKG-INFO0000644000175000017500000000057712061403162014514 0ustar simonsimon00000000000000Metadata-Version: 1.0 Name: bpython Version: 0.12 Summary: Fancy Interface to the Python Interpreter Home-page: http://www.bpython-interpreter.org/ Author: Bob Farrell, Andreas Stuehrk et al. Author-email: robertanthonyfarrell@gmail.com License: MIT/X Description: bpython is a fancy interface to the Python interpreter for Unix-like operating systems. Platform: UNKNOWN bpython-0.12/light.theme0000644000175000017500000000112212031025543015535 0ustar simonsimon00000000000000# Each letter represents a colour marker: # k, r, g, y, b, m, c, w, d # which stands for: # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default # Capital letters represent bold # Copy to ~/.bpython/foo.theme and set "color_scheme = foo" in # ~/.bpython/config [syntax] keyword = M name = r comment = b string = g error = r number = B operator = c paren = b punctuation = b token = g [interface] # XXX: gnome-terminal appears to be braindead. The cursor will disappear unless # you set the background colour to "d". background = w output = b main = b prompt = r prompt_more = g bpython-0.12/ROADMAP0000644000175000017500000000100612031025543014411 0ustar simonsimon00000000000000Roadmap ======= v0.9.7 ------ bpython - Create ~/.bpython on install (#17) - With a wizard - Support for invoking with environment/namespace setup (#76) - Official windows support (it segfaults with sys.stderr being redirected) - Import completion advanced (#48) bpython.gtk_ - Fix fonts in bpython.gtk_ (default monospace, etc) - Add shortcut keys to bpython.gtk_ - Show a status bar thingy in bpython.gtk_ - Colourise docstring (#80) common - test suite v1.0 ---- - Ditch curses for an urwid/twisted loop