--- a/vim/.vimrc Tue Nov 16 17:53:55 2010 -0500
+++ b/vim/.vimrc Mon Nov 22 14:32:21 2010 -0500
@@ -272,8 +272,10 @@
map <F4> :TlistToggle<cr>
map <leader>T :!/usr/local/bin/ctags --exclude='**/ckeditor' -R . $(test -f .venv && echo ~/lib/virtualenvs/`cat .venv`)<CR>
-" Rope
-source $HOME/.vim/sadness/ropevim/rope.vim
+" Rope and Bike.
+let g:bike_exceptions=1
+source $HOME/.vim/sadness/sadness.vim
+
let ropevim_enable_shortcuts = 0
let ropevim_guess_project = 1
noremap <leader>rr :RopeRename<CR>
--- a/vim/colors/molokai.vim Tue Nov 16 17:53:55 2010 -0500
+++ b/vim/colors/molokai.vim Mon Nov 22 14:32:21 2010 -0500
@@ -58,7 +58,7 @@
hi Macro guifg=#C4BE89 gui=italic
hi SpecialKey guifg=#66D9EF gui=italic
-hi MatchParen guifg=#E4E400 guibg=NONE gui=bold
+hi MatchParen guifg=#E4E400 guibg=#232728 gui=bold
hi ModeMsg guifg=#E6DB74
hi MoreMsg guifg=#E6DB74
hi Operator guifg=#F92672
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/AUTHORS Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,24 @@
+Authors
+-------
+
+Phil Dawes <pdawes@users.sourceforge.net> - Current Maintainer
+ and Main Author
+Shae Erisson <shae@lapland.fi> - Original Maintainer
+
+
+The following people have contributed code, bugfixes and patches:
+
+Jürgen Hermann <jh@web.de>
+Canis Lupus
+Syver Enstad <syver-en@online.no> Windows emacs patches
+Mathew Yeates <mathew@comma.jpl.nasa.gov> VIM support and bug fixes
+Marius Gedminas <mgedmin@delfi.lt> More VIM support
+François Pinard <pinard@iro.umontreal.ca> Pymacs + help with
+ emacs integration
+Ender <ender@objectrealms.net>
+Jonathan <jonathan@infogen.net.nz>
+Steve <thepindropper@yahoo.com.au>
+Peter Astrand <peter@cendio.se>
+
+See ChangeLog for more details.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/COPYING Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,32 @@
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 2000 by Shae Erisson <shae@lapland.fi>
+Copyright (c) 2001 by Phil Dawes <pdawes@users.sourceforge.net>
+
+All rights reserved.
+
+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, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+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
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/ChangeLog Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,2034 @@
+2004-02-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added feature to
+ resolveImportedModuleOrPackage that searches the package hierarchy
+ of the scope even if it isn't in the pythonpath.
+
+2004-02-11 Phil Dawes <pdawes@users.sf.net>
+
+ * bike/query/getTypeOf.py: rewrote resolveImportedModuleOrPackage
+ to use purely getModuleOrPackageUsingFQN searches. (doesnt use
+ getTypeOf Root searching any more)
+
+ * bike/query/common.py: rewrote getLogicalLine to handle
+ multilines that parse seperately. (But was submitted by Peter
+ Astrand)
+
+2004-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added functionality to search from the
+ current directory if looking for an import and scope is a module
+
+2004-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/moveToModule.py: Added handling of individual
+ 'from a.foo import theFunction'.
+
+ * bike/parsing/fastparserast.py: Added Peter Astrand's patch to
+ fix following bug:
+ 'Find-References (and probably others) fails if there is more
+ whitespace than a single space between the "class"/"def" keyword
+ and the class/def name.'
+
+ * bike/query/common.py: fixed bug in just-written-code where no
+ dest in Printnl causes npe.
+
+ * bike/query/common.py: Fixed a bug in MatchFinder. Printnl ast
+ nodes (print >>foo, bah) look at there children the opposito way
+ to the way the text comes. (i.e. they do visit(bah), visit(foo) in
+ the example above). Wrote code to get round this.
+
+2004-02-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/moveToModule.py: added more functionality to
+ moveFunction
+
+ * bike/bikefacade.py: added expanduser to normalizeFilename so
+ that ~/src/foo gets turned into a proper full path.
+
+ * bike/bikefacade.py: exposed moveClassToNewModule.
+
+ * bike/refactor/moveClass.py: started move-class-to-new-module
+ refactoring.
+
+2004-02-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/pathutils.py: new module containing all the
+ functions that search all the files in the pythonpath.
+
+ * bike/parsing/extended_ast.py: Removed this module. SourceFile is
+ now in load.py, the rest is in fastparserast.py
+
+ * ide-integration/BicycleRepairMan_Idle.py : Added patch from
+ Steve <thepindropper@yahoo.com.au> to allow easier saving of files
+ before refactoring in IDLE
+
+2004-02-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: lots of cleanup. Renamed Source
+ class to SourceFile. Slimmed down Package and Root. Am planning on
+ removing this module completely, and moving the classes into other
+ modules.
+
+
+ * bike/refactor/extractVariable.py: refactored and removed
+ extractLocalVariable_old
+
+ * bike/refactor/extractMethod.py: refactored and removed
+ extractMethod_old
+
+ * bike/refactor/test_rename*.py: Removed the 'rename twice'
+ tests. They are no longer applicable (since BRM saves to disk
+ after each refactoring)
+
+ * bike/transformer/: Moved save.py and undo.py into the
+ transformer package
+
+ * bike/parsing/newstuff.py: moved generatePackageDependencies into
+ the parsing package, because it needs to be used by other modules
+ in the parsing package.
+
+2004-02-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added generatePackageDependencies
+ which given a file in a package hierarchy, yields a list of
+ external packages and modules used by the package.
+
+ * bike/parsing/newstuff.py: Rewrote
+ generateModuleFilenamesInPythonPath wrt new ref search scheme.
+ Scheme: If the module you are searching from is in a package
+ hierarchy, scan every module in the hierarchy, and the files in
+ the directory above it (since they can reach the package without
+ including it in PYTHONPATH).
+ If the module is in a non-package directory, search in all files
+ in the directory, and in any packages below that directory.
+ N.B. this is in addition to directories in the PYTHONPATH.
+
+2004-02-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Removed getRegexMatchesInPythonPath
+
+ * bike/parsing/newstuff.py: Modified getSourceNodesContainingRegex
+ to search the pythonpath + the set of python files in the
+ directory above the root package of the current file. This should
+ remove the requirement to add the root directory to the python
+ path, and thus prevent BRM from finding references in other
+ package structures.
+
+ * bike/query/common.py: Removed scanASTSourceNodesForMatches and walkSourceNodes
+
+ * bike/globals.py: Added True/False declaration for python 2.2
+ back-compatibility.
+
+2004-01-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added code to check for \ in a previous
+ line when locating logical lines
+
+2004-01-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added a test and fixed a bug that meant
+ find-definitions from multiline statements wouldn't work if the
+ braces were equalised on the following line.
+ (thanks again to Detlev for reporting this bug)
+
+2004-01-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Added a test and fixed a bug that meant
+ find-definitions from multiline statements wouldn't work.
+ (thanks to Detlev for reporting this bug)
+
+2004-01-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added Detlevs patch to not recurse into
+ subversion directories.
+
+2004-01-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Added code in
+ generateModuleFilenamesInPythonPath to ensure each filename is
+ generated once. (and added test to check for it)
+ (Thanks to Detlev & Syver for this bug report)
+
+ * bike/query/common.py (globalScanForMatches): Removed old
+ ast-based code from globalScanForMatches
+
+2004-01-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug where 'from foo import aou'
+ would cause an assert to fail if foo didn't exist. (Thanks Syver)
+
+2004-01-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added code to normalize paths coming into
+ BRM. (hopefully fixes Syver's problems on windows - Thanks Syver)
+
+
+2003-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug in
+ Bicyclerepair_idle.
+
+----------------------- 0.9 BETA4 -------------------------------
+
+2003-09-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug where brmctx
+ was being tested for a __nonzero__ operator due to new wrapper
+ stuff.
+ Also made the matches window a singleton
+
+ * bike/query/findReferences.py: Rewrote attribute reference
+ finding algorithm to locate a possible match, and then check if
+ the match class shares a common ancestor with the target. (rather
+ than finding all classes in the target hierarchy and then testing
+ each match class against the hierarchy).
+ This results in a 10000%
+
+2003-09-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added buildClassHierarchy
+ functionality to guess a class hierarchy, but found that it's
+ still much too slow to handle the wxpython class hierarchy of 1900
+ odd classes under 1 root!
+
+
+2003-09-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/fastparserast.py: Added maskedsrc to the cache
+
+ * bike/query/getTypeOf.py: Added caching of type lookups
+
+ * bike/bikefacade.py: Added a generic wrapper which purges the
+ caches before and after each brm ctx call
+
+2003-08-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/newstuff.py: Added simple caching mechanism for
+ sourceNodes
+
+2003-08-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Removed some crufty methods and
+ base classes for managing the ast tree hierarchy
+
+ * bike/bikefacade.py: Removed a couple of ast related methods
+
+ * bike/query/findReferences.py: Modified the findRefs
+ functionality to exclude matches where the confidence is high that
+ it's *not* the right type.
+
+ * bike/parsing/load.py: removed the load function (and made
+ necessary refactorings to remove it)
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added setWarningLogger
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Fixed bug where "../.." was being
+ added to sys.path. This was because extended_ast.py was adding
+ importing setpath.py.
+
+2003-08-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added warning code to print message if
+ sys.path is changed by setpath.py
+
+ * ide-integration/test/README: Added ide tests for inline and
+ extract variable
+
+ * bike/bikefacade.py: Removed the load() function, and fixed
+ inline and extract local variable
+
+----------------------- 0.9 BETA3 -------------------------------
+
+2003-08-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Fixed removeLibdirsFromPath to work with
+ windows python lib directories
+
+----------------------- 0.9 BETA2 -------------------------------
+
+2003-08-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/*: Made it backward compatible with python 2.2
+
+ * ide-integration/bike.vim: Adapted vim plugin to work with new
+ api.
+
+----------------------- 0.9 BETA1 -------------------------------
+
+2003-08-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/test_*: Misc fixes for windows paths
+
+ * ide-integration/BicycleRepairMan_Idle.py: Cleaned up error
+ handling for undo stack
+
+ * bike/query/*: Misc fixes for tests
+
+ * bike/refactor/extractMethod.py: Updated to work with new
+ non-tree system
+
+2003-08-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: Converted createSourceNodeAt to create a
+ directory structure rather than an ast. (and broke a load of tests)
+
+2003-08-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: added test setup fixture to change directory
+ before executing tests
+
+2003-08-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug which was stopping sub
+ packages from being found properly
+
+ * bike/query/relationships.py: Fixed getAllClassesInHierarchy and
+ getAllDerivedClasses to work without AST. Uses a smart algorithm
+ which scans the text of files with regexes for baseclassnames and
+ import aliases to narrow down the likely candidates.
+
+
+2003-08-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Updated docs for python2.3/idlefork
+
+2003-08-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added code to just use
+ 1 window for matches, and to reopen it if needed after it is closed
+
+2003-08-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Fixed bug which cause BRM to descend into
+ non-package directorys
+
+ * bike/parsing/extended_ast.py: Added a couple of new functions to
+ handle pythonpaths for queries and refactorings
+
+2003-08-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: modified globalScanForMatches to scan
+ files in addition to walking the ast.
+
+ * bike/query/findReferences.py: Added code to work with new
+ stateless design.
+
+2003-07-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Removed module locating code from
+ findDefinition, and made it use resolveImportedModuleOrPackage
+ instead.
+
+ * bike/query/getTypeOf.py: Modified resolveImportedModuleOrPackage
+ to use the newstuff function getModuleOrPackageUsingFQN if it cant
+ locate the module in the AST tree.
+
+ * bike/parsing/newstuff.py: modified getModuleUsingFQN
+ to handle packages. renamed to getModuleOrPackageUsingFQN
+
+2003-07-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Made necessary modifications
+ enable finding of function definitions without importing code
+ (uses the python path to determine where to look). Doesnt work for
+ methods yet.
+
+2003-07-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/inlineVariable.py: Applied Jonathan's patch which
+ fixes bug when multiple instances of a variable are on one line.
+
+2003-06-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: changed findReferences interface
+ to take a filename instead of a srcnode
+
+ * bike/query/findReferences.py: Fixed bug where error wasnt being
+ reported properly
+
+2003-06-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: renamed findReferences to
+ findReferences_old, and created a new findReferences which takes a
+ filename instead of the srcnode
+
+ * bike/bikefacade.py: Removed getFullyQualifiedNameOfScope - it's
+ not used anywhere, and relies on the fqn stuff working.
+
+ * all tests: attempted to remove all usage of fqn from
+ tests. Instead, replaced with name and filename tests. This is
+ because fqn is one of the major things binding the scopes
+ together.
+
+ * bike/bikefacade.py: removed getTypeOfExpression. It's not used
+ anywhere. Will reinstate when it is required again.
+
+ * bike/query/getTypeOf.py: Fixed bug where a recursive function
+ could cause BRM to stack overflow
+
+2003-06-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added resolveImportedModuleOrPackage fn
+ to try and split the number of calls to getTypeOf()
+
+ * bike/query/getTypeOf.py: Fixed bug where a[0].theMethod() would
+ cause brm to barf.
+
+ * bike/parsing/fastparserast.py: Beefed up the __str__ members for
+ fastparser nodes
+
+2003-06-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Removed output.py. (functionality now in
+ save.py)
+
+2003-06-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: removed the ismodified stuff, in
+ an attempt to make the xast hierarchy a read-only resource.
+ Added a hook in save.py to update the xast whenever somebody
+ queues an update to be saved.
+
+ * bike/parsing/save.py: Added new save module which maintains its
+ own queue of src to be written back to disk. Replaces output.py
+
+2003-05-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/undo.py (UndoStack.undo): refactored to use dictionary
+
+ * bike/query/findDefinition.py: Refactored main function to have a
+ 'stateless' interface (i.e. no need to pass a context).
+ findAllPossibleDefinitionsByCoords(filepath,lineno,col)
+
+2003-05-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py (getRoot): Made the Root object a
+ singleton. Use the getRoot() method to get it. This is an
+ intermediate step in making the parser stateless. (idea is to make
+ it pretend to be stateless by being a singleton, then once the
+ interfaces have changed, transition the parser code to actually
+ make it stateless)
+
+2003-04-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added exception catching and error
+ reporting to bike-emacs integration
+
+2003-03-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * Applied Jonathan's patches
+ - InlineVariable handles multiline statements
+ - Fix to bike vim integration
+
+2003-03-31 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Removed unnecessary argument
+ to BikeRename().
+
+ * bike/query/common.py: Fixed handling of lambdas in MatchFinder.
+
+2003-03-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractVariable.py: Made a start on the
+ extract-local-variable refactoring
+
+2003-03-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/inlineVariable.py: Made a start on the
+ inline-local-variable refactoring
+
+----------------------- 0.8 BETA2 -------------------------------
+
+2003-03-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed bug where
+ paths with spaces meant that you couldn't select a reference when
+ doing a findReferences. (Thanks to Andy Bulka for the report)
+
+ * bike/query/getTypeOf.py: Added infinite recursion protection to
+ getTypeOf
+
+ * bike/query/relationships.py: Added some performance improving
+ code when searching for a classhierarchy. Rather than doing a
+ getTypeOf() on every base class, it finds the strings likely to be
+ base classes (i.e. the name of the base class, and any 'import
+ name as foo' lines) to narrow the search.
+
+2003-03-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug in extract method,
+ where blank lines were messing up the indentation in the resultant
+ block
+
+
+----------------------- 0.8 BETA1 -------------------------------
+
+2003-03-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Fixed bug in brm with emacs on
+ windows, where the mark can be active and nil at the same time.
+
+ * bike/bikefacade.py (BRMContext_impl.load): Fixed bug which
+ affected windows users. Path needs to be saved after it's been
+ normalized.
+
+2003-03-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: fixed bug in attribute finding
+ code - was only searching the first function
+
+ * bike/query/getTypeOf.py: Fixed bug where x = x.bah() would cause
+ recursion error. (just catches the stack overflow)
+
+2003-03-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/*: implemented automatic importing of changed files. This
+ means that ide integration stuff no longer has to import code into
+ brm whenever it is saved.
+
+2003-02-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.vim: Consolidated RenameMethod,
+ RenameFunction and RenameClass into 1 menu option. This is because
+ brm now supports renaming of variables and attributes and I didn't
+ want to add another 2 menu items.
+
+
+2003-02-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Fixed rename 'prompt'
+ bug. Consolidated all the rename stuff into 1 menu option.
+
+2003-02-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/*: removed getReferencesToClass/Function/Method and
+ replaced with findReferences
+
+2003-02-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: Added findReferencesIncludingDefn
+ and made 'vanilla' findReferences not return the definition. This
+ paves the way for a unified rename that can be used to rename
+ anything.
+
+2003-02-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Fixed bug reported by Marius - if
+ class is declared in __init__.py, it blows up.
+
+ * bike/query/findReferences.py: Fixed bugs in
+ 'getDefinitionAndScope' logic, so can handle nested classes etc..
+
+2003-02-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToClass.py: Replaced module logic
+ with call to findReferences()
+
+ * tests: modified or removed tests that tested for the erroneous
+ 'import a.b.bah.TheClass'. (A class or function can't be imported
+ - only a module)
+
+2003-01-24 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Show the line itself after finding
+ references/definition.
+
+2003-01-23 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py: Fixed a trivial NameError in
+ MatchFinder.visitGlobal().
+
+2003-01-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Refactored and cleaned up the code in
+ this module.
+
+2003-01-22 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py: Fixed another ValueError, this time on
+ "from foo import bar" lines.
+
+2003-01-20 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/query/common.py, bike/query/findDefinition.py,
+ bike/query/findReferences.py: Fixed ValueError: list.index(x): x not
+ in list" error caused by several visitFunction methods visiting
+ their child nodes (argument names and default values) out-of-order.
+
+2003-01-16 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/refactor/extractMethod.py: Now puts spaces after commas in
+ generated code.
+
+2003-01-15 Marius Gedminas <mgedmin@delfi.lt>
+
+ * ide-integration/bike.vim: Better load failure diagnostics.
+
+2003-01-14 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/bikefacade.py, bike/parsing/fastparserast.py,
+ ide-integration/BicycleRepairMan_Idle.py: CRLF -> LF translation
+
+2003-01-13 Marius Gedminas <mgedmin@delfi.lt>
+
+ * bike/bikefacade.py: Added a function to check whether a given file
+ was ever imported.
+
+ * bike/test_bikefacade.py: Unit test for the above.
+
+ * ide-integration/bike.vim: Added code to watch for modified files
+ and reimport them into if they had been imported previously.
+
+2003-01-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToModule.py: Added Ender's
+ getReferencesToModule module (and test module)
+
+2003-01-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Added some find class attribute
+ definition functionality
+
+2003-01-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: fixed bug where 'import'
+ statements in fn scopes weren't being searched
+
+ * ide-integration/bike.vim: Modified Marius' bike.vim vi
+ integration to support new rename and findReferences calls.
+
+2003-01-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added fix to redisplay frame in
+ emacs, and to test for mark correctly in emacs.
+
+2003-01-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * AUTHORS: Added Mathew and Marius
+
+2003-01-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.emacs: Added simple instructions for installing pymacs.
+
+ * ide-integration/Pymacs-0.20: I'm going to ship pymacs-0.20 with
+ bicyclerepairman. I've modified it a little to make installation
+ easier.
+
+ * ide-integration/bikeemacs.py: Moved bikeemacs back to its
+ original place.
+
+
+2003-01-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Cleaned this up a bit
+
+ * bike/parsing/fastparser.py: Fixed bug reported by Mathew Yeates
+ where more than one space between 'class' and the name foxed the
+ parser.
+
+2002-12-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findReferences.py: Started work on unified
+ findReferences code. (uses findDefinition to compare matches)
+
+ * ide-integration/emacs/bikeemacs.py: Rewrote emacs integration
+ using the excellent pymacs package
+
+2002-12-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Fixed findDefinition
+ cosmetic bug
+
+ * ide-integration/bike.el, bikeemacs: Added Syver Enstad's patch
+ for allowing filenames with spaces
+
+2002-12-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Completed support for
+ findDefinition
+
+ * ide-integration/bike.el: added code to handle the fact that if
+ you select a region, the point is one greater than the end of the
+ region and so misses it.
+
+2002-12-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/findDefinition.py: Added code to scan for other
+ method matches after locating the 100% one
+
+2002-12-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added basic
+ finddefinition support to idle
+
+ * ide-integration/bikeemacs.py: Added finddefinition support to xemacs
+
+ * bike/bikefacade.py: Exposed findDefinition through bikefacade
+
+ * bike/query/findDefinition.py: Added new query interface for
+ finding the definition of a reference, given its line/col position
+ in a module. Just supports function, method, and class references
+ for now.
+
+---------------------- 0.7 ---------------------------------------
+
+2002-11-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Fixed bug where saving buffers caused
+ the new emacs protocol to get in a tangle. My solution was to add
+ a disableMessages capability
+
+---------------------- 0.7RC3 ------------------------------------
+
+2002-11-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added acknowledgement protocol, to
+ get round synchronisation problems in windows emacs
+
+2002-11-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing: decommissioned addtypeinfo, typeinfo, tokenutils,
+ tokenhandler, matchTokensToAST, doublelinkedlist, testdata
+
+ * bike/query/getReferencesToClass.py: Fixed bug where
+ 'from foo import *' would cause an exception if searching for a
+ class called 'foo'
+
+ * bike/*/test_*.py: Removed dependency on brmtransformer.
+
+2002-11-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Rewrote extract method module to
+ use all the new cool stuff (and operate or strings). Doesnt use
+ linked lists, iterators, tokens, brmtransformer or any of that
+ shite any more.
+
+2002-11-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Fixed bug where classscope was being
+ searched if the name wasnt found in methodscope. This is incorrect
+ because class scope isn't visible except through 'self' or a fully
+ qualified classname.
+
+2002-11-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Re-wrote MatchFinder to scan along the
+ source code text when visiting ast nodes. This removes the need to
+ match tokens in order to find the source code location of an ast
+ node.
+
+---------------------- 0.7RC1 ------------------------------------
+
+
+2002-11-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/relationships.py: Added performance enhancement to
+ getAllDerivedClasses. Now does string search on file before ast
+ searching the classes.
+
+2002-10-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Finished
+ findReferencesByCoordinates support in idle.
+
+2002-10-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added trace
+ console. Started integration of findReferencesByCoordinates into
+ idle.
+
+ * ide-integration/bike-emacs.py: Added xemacs support for
+ getFullyQualifiedNameOfScope, getTypeOfExpression and
+ findReferencesByCoordinates
+
+
+2002-10-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added getFullyQualifiedNameOfScope,
+ getTypeOfExpression and findReferencesByCoordinates
+
+2002-10-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: changed promptForRename callback signature
+ to be linenumbers and columnnumbers
+
+ * bike/refactor/renameMethod.py: Added prompt
+ functionality.
+
+2002-10-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Added functionality to handle scanning
+ for types of references (needed by getReferencesToMethod)
+
+ * bike/query/getReferencesToMethod.py: Implemented to use
+ common.py stuff
+
+2002-09-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/common.py: Split out common query code into common.py
+
+2002-09-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameFunction.py: Now uses the getReferences stuff
+
+2002-09-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/transformer/WordRewriter.py: Split this class out of the
+ renameClass module.
+
+ * bike/transformer: Added new transformer package to contain code
+ which rewrites the internal representation of the sourcecode
+
+ * bike/bikefacade.py: Implemented locateNode to use the new
+ fastparserast stuff.
+
+ * bike/parsing/extended_ast.py: Added code to set fqn (fully
+ qualified name) attributes on the fastparser ast
+
+2002-09-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Finished modification to use
+ getReferencesToClass.
+
+2002-09-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getTypeOf.py: Refactored getType() functionality out
+ of ast classes and into a query module.
+
+ * bike/parsing/fastparser.py: Added parsing of import and from
+ lines to fastparser, and optimised a little.
+
+2002-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Started modification to use
+ getReferencesToClass.
+
+2002-08-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/query/getReferencesToClass.py: New module which returns a
+ sequence of references to a class
+
+ * bike/parsing/fastparser.py: Wrote new parser which builds a tree
+ of functions and classes, very quickly.
+
+2002-08-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Fixed bug where attempting to
+ tokenize small strings would result in attributeerror
+
+2002-08-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/logging.py: Logging module. Eventually will be replaced by
+ import logging, but for now is a copy of the code by Vinay Sajip
+ (to save people from having to download the module)
+
+ * bike/parsing/brmtransformer.py: Added simple fix - tuple being
+ concatinated to list was causing problems
+
+
+---------------------- 0.6.7 ------------------------------------
+
+2002-07-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added Canis fix for
+ the bug which stopped brm from displaying the menu when starting
+ idle via right-click file.
+
+ * bike/bikefacade.py: Added Canis windows compatability fix for
+ dealing with .pyw files
+
+
+2002-07-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug where extracting an
+ expression from a line just below a block opener (e.g. if foo:)
+ would cause a parse error.
+
+2002-07-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Doc about problems on windows
+
+---------------------- 0.6.6 ------------------------------------
+
+2002-07-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/doublelinkedlist.py: Optimised to use a list rather
+ than a class instance for each link element.
+
+2002-07-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/typeinfo.py: Fixed bug - package wasn't checking
+ parent packages (and Root) for types. This meant that modules
+ imported from higher up the tree weren't being found.
+
+ * bike/parsing/extended_ast.py: Renamed 'Top' to 'Root'
+
+---------------------- 0.6.5 ------------------------------------
+
+2002-07-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Minor bug fix
+
+2002-07-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bikeemacs.py: Added code so that it waits for
+ buffer to be reloaded in emacs before sending the request to
+ update the next one.
+
+ * ide-integration/bike.el: Added bug fix - if prompt for rename
+ was on file not loaded, emacs would send a message back to brm
+ when it ran find-file, which buggered up the 'rename-this-method?'
+ interaction.
+
+2002-07-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Added support for GNU emacs
+
+ * README.emacs: renamed from README.xemacs
+
+ *
+
+2002-07-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Added code to handle more windows filename
+ brokenness
+
+2002-07-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added same optimisation to
+ renameMethod.
+
+ * bike/refactor/renameClass.py: Added optimisation: source string
+ is scanned for classname before visiting ast. (with the lazy ast
+ creation this means that files aren't parsed unless they contain
+ the keyword (or are linked to the source via function call).
+
+ * bike/parsing/extended_ast.py: Refactored .source member into
+ .getSource() method and implemented so that tokens are
+ synchronised with source if they're modified.
+
+2002-07-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Added lazy parsing of ast when
+ source.getModule() is called.
+
+ * bike/parsing/typeinfo.py: Added PackageTypeInfo which deduces
+ child types by interrogating the package rather than relying on
+ children to register themselves in the typeinfo. This was required
+ to facilitate lazy parsing of ast. (since parse would have had to
+ be done to add module node to package). A lookup of a module now
+ causes getModule() to be called on the sourcefile, thus triggering
+ parse of source into ast.
+
+---------------------- 0.6.4 ------------------------------------
+
+2002-06-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Added bug fix for superflous
+ brackets. Basically, if brackets are unecessary (e.g. '(a).foo()')
+ then they aren't in the parse tree but are in the token
+ stream. This buggers up the token matching.
+ This fix just ignores unmatching ')'s. It was the simplist thing
+ that could possibly work, but may cause problems later - needs
+ lots of testing.
+
+---------------------- 0.6.3 ------------------------------------
+
+2002-06-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Fixed bug where getattr renamed
+ all occurences of the methodattr in the rest of the tokens
+
+2002-06-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed bug where commend before
+ the extracted block would make brm think it should be extracting
+ an expression rather than a block.
+
+ * bike/parsing/addtypeinfo.py: Fixed the same bug in _getImportedType
+
+ * bike/refactor/renameClass.py: Fixed bug where the module search
+ routine in the 'From foo import *' code was using the parent scope
+ as the search position. This doesnt work if 'from' is nested in a
+ class since parent scope is a module, not a package. Added method
+ _getParentPackage() to handle this correctly.
+
+2002-06-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed the 'loop' bug. extract
+ method will now recognise variables assigned to by the extracted
+ function and used in a loop by the calling function.
+
+2002-06-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Lots of refactoring prior to
+ fixing a bug.
+
+2002-06-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/doublelinkedlist.py: Rewrote 'insert' so that it
+ doesnt move the iterator. Renamed old method to
+ insertAndPositionIteratorOnNewElement.
+
+2002-06-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Fixed the parse error when
+ extracting code from an if...else... block. The problem was that
+ the code below the extraction contained the 'else', but not the
+ 'if'. This is hacked around by inserting an 'if 1: <indent>
+ pass'. This seems to work, but I welcome a better way of handling
+ this.
+
+ * bike/parsing/output.py: Added code to ensure that indent never
+ dips below 0. This fixes a bug in parsing during method extracts
+ out of nested blocks.
+
+ * bike/refactor/extractMethod.py: Fixed the 'don't know what type
+ this isAssAttr(Name('item'), 'decl', 'OP_ASSIGN')' bug
+
+---------------------- 0.6 --------------------------------------
+
+2002-05-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/test/*: Added manual test script for testing ide
+ integration before a release
+
+ * ide-integration/BicycleRepairMan_Idle.py: Now imports code into
+ brm when loaded, or saved for the first time.
+
+ * ide-integration/bike.el: Now imports all loaded python files
+ into brm. Also checks to see if file was just updated to avoid
+ superflous reloads.
+
+2002-05-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/bike.el: Added menu for brm
+
+ * bike/parsing/undo.py: Added an internal undo buffer size
+
+ * ide-integration/BicycleRepairMan_Idle.py: Added generic
+ exception reporting to idle
+
+ * ide-integration/bikeemacs.py: Added generic exception
+ reporting. All exceptions are handled and reported back to the
+ user.
+
+2002-05-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ide-integration/BicycleRepairMan_Idle.py: Exposed undo to idle
+
+ * ide-integration/bikeemacs.py: Exposed undo to emacs
+
+ * bike/bikefacade.py: Exposed undo functionality to facade
+
+ * bike/parsing/undo.py: Added undo functionality
+
+2002-05-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/test_extractMethod.py: Fixed bug in expression
+ extracting code (which caused a parse exception with correct code)
+
+ * bike/bikefacade.py: Added public interface
+
+---------------------- 0.5 --------------------------------------
+
+ * ide-integration: Created new directory for integration modules
+
+2002-05-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Exposed extract function to idle
+
+ * bike/ui/bikeemacs.py: Exposed extract function to emacs
+
+ * bike/refactor/extractMethod.py: Added support for extracting
+ expressions into methods (e.g. a = 1+2 -> a = self.oneplustwo())
+ Added support for extracting functions.
+
+2002-05-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Extract Method pretty much
+ finished. Now on with the testing...
+
+2002-05-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added extract method support
+ for idle
+
+2002-05-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bike.el: implemented extract-method for emacs
+
+ * bike/bikefacade.py: Exposed extract-method to the outside world
+
+
+2002-05-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/extractMethod.py: Implemented simple extract
+ method
+
+2002-04-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/matchTokensToAST.py: The 'new' addtokens. Instead
+ of each AST node getting its own slice of tokens, there is only
+ one token list and each node has 2 linked list iterators pointing
+ to the start and end of its tokens in within it.
+ Thus the process of calculating the position of these iterators is
+ now called 'token matching' rather than 'adding tokens to ast'.
+
+ Having only 1 token stream vastly simplifies things. There are no
+ consistency problems when renaming or moving tokens, and no need
+ to 'divide' the comments amongst the AST nodes.
+ This enables one matching algorithm to be able to cater for all
+ ast nodes without specialized logic.
+
+ * bike/parsing/vector.py, bike/parsing/doublelinkedlist.py:
+ containers which support the same bi-directional iterator
+ interface. Used in refactoring the parsing module to use a linked
+ list of tokens
+
+2002-04-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/bikefacade.py: Removed use of tokens
+
+---------------------- 0.4.3 ------------------------------------
+
+2002-04-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: fixed import problem in
+ windows
+
+ * bike/parsing/load.py: Added code so that if you select a
+ directory it will load all the modules and packages in it even if
+ it isnt a package (i.e. doesnt have a __init__.py), but would
+ recurse into non-package sub directories.
+
+ * bike/ui/*: This is no longer a package, since the only 2 files
+ are installed elsewhere.
+
+ * setup.py: Made BicycleRepairMan_Idle.py a root level
+ module. This means it can be used with idle by just adding
+ [BicycleRepairMan_Idle] to your .idle file.
+
+
+2002-04-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Addtokens class now takes tokens in
+ constructor. The addTokens function is renamed to
+ addTokensToASTFromSrc. This will pave the way for Source nodes to
+ hold tokens rather than the source as a string
+
+ * bike/parsing/tokenhandler.py: Fixed bug which caused name ast
+ node to not be tokenized
+
+ * bike/parsing/load.py: Added check for __init__.py before
+ recursing into sub directories. This means that non-package sub
+ directories will not be recursed into. (i.e. the user must load
+ those seperately)
+
+
+2002-03-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * NEWS: consolidated all README-version files into one NEWS file
+
+ * README*: updated documentation
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed another windows filename
+ isnt normalized bug
+
+ * bike/ui/bike.el: Added same functionality as for idle
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added load on save
+ functionality - means each saved python file gets automatically
+ imported into bicyclerepairman.
+
+2002-03-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added functionality to deduce the package
+ of the loaded module/package by inspecting parent directories for
+ __init__.py modules. This allows adding of new modules to an
+ existing AST.
+
+2002-03-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * README.idle: Added Evelyn's ammendment to doc
+
+---------------------- 0.4.2 ------------------------------------
+
+2002-03-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * setup.py: Removed Icons stuff from setup
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed some bugs with idle
+ integration in windows:
+ - Ask if should rename dialogs dont take focus (which
+ makes selection disappear in windows)
+ - Filename normalizing means that filenames get compared correctly.
+
+2002-03-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/testutils.py: Changed the test package structure generation
+ code to add __init__.py modules to the packages. This will be used
+ to work with the new loader code (when I write it).
+
+2002-03-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed bug which was stopping
+ changed files from being reloaded on windows
+
+---------------------- 0.4.1 ------------------------------------
+
+2002-03-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/BicycleRepairMan_Idle.py: Fixed bug where rename method
+ prompts weren't visible to the user.
+
+ * bike/parsing/addtypeinfo.py: Fixed a bug which meant that
+ sourcenode wasnt getting added to default arguments, which caused
+ brm to crash when attempting to tokenize these.
+
+
+---------------------- 0.4 --------------------------------------
+
+2002-03-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * setup.py: Removed bikegui and pyxmi from the setup
+ program. These are now deprecated.
+
+ * bike/ui/BicycleRepairMan_Idle.py: Added the idle support module
+ to cvs.
+
+ * bike/parsing/tokenhandler.py: Simplified SimpleTokenHandler
+
+ * bike/parsing/extended_ast.py: Removed ExtendedNode base class -
+ it isnt needed (thanks Evelyn)
+
+ * bike/parsing/tokenutils.py: fixed bug in
+ _getOffsetOfTokensCorrespondingToParseTree which was including the
+ NL token in the offset it returned.
+
+ * bike/bikefacade.py: Changed signature of rename method
+ callback. It now sends filename and coords as args rather than
+ exposing the ast nodes to the client.
+
+2002-03-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/__init__.py: Moved bikefacade.py (from the ui package) into
+ the bike package. This is now referenced from __init__.py so a
+ client can do:
+ import bike; ctx = bike.load("mypath"); ctx.renameMethod()
+
+
+ * bike/parsing/typeinfo.py: ModuleTypeinfo.getTypeOf()
+ functionality now uses the imported module *name* to check other
+ modules, rather than a reference to the module itself. This is to
+ allow module reloading.
+
+ * bike/parsing/extended_ast.py: Added 'addtypeinfo' call to Source
+ node. This is now done when the sourcenode is added to the parent
+ node rather than being run once over the whole tree. This
+ facilitates adding additional trees to the ast without having to
+ run the whole addtypeinfo step again.
+
+ * bike/parsing/addtypeinfo.py: Removed the subclasses stuff from
+ the classtypeinfo functionality. Doing this at initial-parse time
+ is too brittle, since a reload of a child source node could remove
+ (or add) a subclass from a base class.
+ Instead, subclasses are calculated at refactoring time.
+
+
+
+2002-03-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Removed addfqn,
+ addparentscopeToNode and addSourcenode to nodes functionality from
+ the SourceModule, and integrated them with the first 'addtypeinfo'
+ pass. This almost halves the initial parsing time.
+
+
+2002-03-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Changed renameMethod signature to
+ take a method fqn rather than class and method parameters. This
+ allows the tests to take advantage of the same signature for
+ renameMethod,renameFunction and renameClass.
+
+ * bike/ui/bikeemacs.py: refactored to use the new bikefacade module
+
+ * bike/ui/bikefacade.py: New module which provides easy interface
+ into brm, for integrating into IDEs.
+
+ * bike/parsing/test_addtypeinfo.py: Added recursion handling for
+ things like a=a() (just catches the runtime error)
+
+2002-02-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: found a problem with lambdas - they
+ take the reference from the outer scope. If the reference is a
+ loop variable (e.g. for i in blah) then it is reassigned which
+ knackers the closure.
+ Solution - lambda i=i: doSomethingWith(i)
+
+ * bike/ui/bikegui.py: removed reliance on tokens
+
+ * bike/parsing/tokenhandler.py: added getStartCoords() and
+ getEndCoords() functions.
+
+
+2002-02-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * */setpath.py: code which insures pythonpath includes path
+ to the bike module (used for running test scripts). This is
+ imported by most modules.
+
+ * bike/parsing/extended_ast.py: moved addParentScopeToNodes()
+ functionality from addtypeinfo into Source class.
+
+ * bike/parsing/addtypeinfo.py: For deduced types (e.g. types got
+ from function calls), the closure of the type deducing function is
+ put in the typeinfo rather than the type itself. The closure is
+ then run at lookup.
+ This facilitates lazy loading, and means source modules can be
+ reloaded without invalidating the typeinfo objects.
+
+2002-02-18 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikeemacs.py: Added reload source functionality
+
+ * bike/ui/bike.el: Added support for renameMethod - prompts user
+ for types that brm can't deduce
+
+2002-02-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikeemacs.py: New support for emacs
+
+ * bike/parsing/addtypeinfo.py: Fixed bug which was causing
+ inheritance specs not to be renamed
+
+ * bike/parsing/output.py: Added some code to preserve level of
+ indent in multiline (comma ended) statements (e.g. function
+ arguments, class inheritence args etc...). This really needs
+ reworking into a general solution, but works 70% of the time for
+ now.
+
+2002-01-31 Phil Dawes <pdawes@bea.com>
+
+ * bike/parsing/output.py: Added comment indenting code which
+ should preserve the indent level of comments
+
+2002-01-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameClass.py: Added functionality to rename the
+ function and class ref in 'import a.b.theFunction and from a
+ import theClass' type statements
+
+ * bike/parsing/renameFunction.py: new refactoring
+
+ * bike/parsing/tokenhandler.py: Added code to update the typeinfo
+ objects of the parent scope and the Top node. This enables renames
+ to be carried out more than once on the same ast
+
+ * bike/parsing/addtypeinfo.py: Removed node.typeinfo.fqn. refactor
+ code should now use node.fqn
+
+2002-01-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Added token functionality to
+ Name. Removed the getPreceedingTokens() stuff - the getTokens()
+ now returns all tokens including preceeding comments, tokens
+ etc... getNodeTokens() returns just the tokens associated with the
+ node. This is consistent across all the tokenhandler base classes.
+
+ * bike/parsing/tokenutils.py: Fixed bug in _appendTokens where
+ the src 'raise foo' would result in 'raise' being picked up when
+ searching for 'foo' because both tokens are of the same type (name).
+
+2002-01-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Added code to class tokenizer to
+ delegate tokens to the baseclass spec (i.e. bah in
+ 'class foo(bah):').
+
+ * bike/parsing/tokenhandler.py: Class handler stuff to output
+ tokens (see above). This means that renameClass now renames class
+ refs in baseclass specs.
+
+2002-01-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Fixed a bug which stopped classes
+ that inherited from classes not in the ast from having method
+ declarations renamed.
+
+2002-01-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Added support for handling line breaks.
+ i.e. if foo and \
+ bah:
+
+ * bike/refactor/renameClass.py: Started new refactoring -
+ renameClass
+
+ * bike/parsing/addtypeinfo.py: removed 'typing indirect recursive
+ functions causes stack overflow' bug
+
+ * bike/refactor/renameMethod.py: Refactored the code so that the
+ renameMethodReferences is done once, with a list of all the
+ classes (in the hierarchy) to match.
+
+
+2002-01-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Now renames methods on all
+ related classes (base classes and sub classes).
+
+ * doc/*: added some html documentation
+
+2002-01-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Did some major refactoring and
+ removed all the code which is functionally duplicated in the
+ addtypeinfo module.
+
+ * bike/parsing/addtypeinfo.py: Spruced up the type inference
+ stuff.
+ ModuleTypeInfo now handles from foo import * by storing a list of
+ other modules to search.
+
+2002-01-17 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Save now returns a list of the files modified
+
+2002-01-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/extended_ast.py: Added lazy tokenization. Source
+ node now parses the source (rather than being handed the ast).
+
+2002-01-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: Added lazy saving. Files are only saved
+ if source modified.
+
+ * bike/testutils.py: Added new utility to find an ast node in a
+ tree based on attributes of the node.
+ e.g. getMatchingASTNode(ast,"Function",name="foo")
+
+2002-01-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added functionality to look for
+ method and ref renames in sub classes
+
+2002-01-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Added support for imports, import
+ from and import as statements
+
+2002-01-09 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenutils.py: Fixed bug where method call was
+ split over 2 lines. The problem is that the tokenlist contains the
+ NL, but the parsetree doesnt, so the matching wasnt working. See
+ addtokens test_doesntBarfWhenMethodCallSplitOverTwoLines for details.
+
+ * bike/parsing/brmtransformer.py: Added 2 classes which override
+ Print and Println, and return the child nodes in the desired order
+ (they are the wrong way round as they come out of
+ ast.py). The print_stmt method returns these.
+
+ * bike/parsing/output.py: Added code to handle spacing with commas
+ and stream operators
+
+2002-01-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/test_renameMethod.py: Refactored tests so that
+ BikeGUI can use them (through template method). Tests are now
+ split into RenameMethodTests,
+ RenameMethodReferenceTests_ImportsClass and
+ RenameMethodReferenceTests_doesntImportClass. This is because
+ bikegui
+
+2002-01-07 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikegui.py: Added dialog to ask user if want to rename
+ method reference (for cases where the type engine can't deduce the
+ type of the instance)
+
+2002-01-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikegui.py: Added a simple gui for renameMethod
+
+2002-01-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load.py: Added functionality to strip of preceeding
+ directories from package structures. (so if you load
+ '/usr/local/python2.2/compiler', the root package is 'compiler')
+
+ * bike/parsing/load.py: Added load_readonly(), which doesn't do the
+ addtokens step.
+ Also added code to enable load() to be called more than once, to
+ add new files and packages to the tree.
+ Moved ui messages into constants module
+
+2002-01-02 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/brmtransformer.py: removed addTokens() step from
+ parse(), so that it must be called seperately. This is because it
+ isn't complete, and imposes quite a performance penalty. (and it's
+ not needed by pyxmi). Updated all the tests to reflect this.
+
+2002-01-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py : Added getSubClasses()
+ functionality to class typeinfo.
+
+2001-12-30 Phil Dawes <pdawes@users.sourceforge.net>
+ * bike/*: Fixed path bugs for windows platform. All tests run on
+ win32 python now.
+
+2001-12-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: Added 'self' type to function
+ typeinfo. This means renameMethod can handle self.theMethod()
+ automatically.
+
+2001-12-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * pyxmi: Wrote little tool to create xmi representation of python
+ code. Suitable for loading into argouml.
+
+ * bike/ui/bikecli.py: Added progress meter support. (also in
+ addtokens.py, renameMethod.py, load.py and output.py
+
+2001-12-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Fixed bug where function call
+ returning instance resulted in crash. 'e.g. e().f()'
+
+2001-12-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtokens.py: Reworked addtokens scheme to 'get all
+ tokens for node, and then pass them to children to take their
+ nodes'. This makes for a much easier time processing tokens.
+
+2001-12-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Changed Getattr rename
+ functionality to work when attribute token isn't second token of
+ node tokenlist
+
+ * bike/refactor/test_renameMethod.py: refactored renameMethod
+ tests into generic abstract base class. These are now used by the
+ ui package (rather than vice versa)
+
+ * bike/ui/bikecli.py: Added code to tell you how many changes have
+ been made, and advice on what might be wrong if this number is 0.
+
+2001-12-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/tokenhandler.py: Added getNodeTokens()
+ implementation to miss out any preceeding cruft not picked up by
+ addtoken functionality on previous nodes.
+
+2001-12-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Changed renameMethodDefinition to
+ work with nested methods (utilising the new typeinfo.fqn stuff).
+ Removed findclass and findmethod.
+
+2001-12-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikecli.py: Modified prompt message a bit. Added actual
+ standard io stuff so that it works with a user input as well as
+ through the test harness.
+
+ * bike/refactor/renameMethod.py: Modified RenameMethodReferences
+ to create a stack of scopes. The code uses the typeinfo from each
+ scope to determine the type of references, and if it can't find
+ the type there, it checks its own references deduced from the
+ classname passed in. I.e. it assumes that the classname is a
+ class, even if it can't find the definition, and renames instances
+ derived from it.
+
+ Also ammended the 'prompt user callback' code to only ask the user
+ if the type engine doesnt know the type of the instance
+ expression. (before now it was prompting if the type was not the
+ same as the input classname)
+
+2001-12-11 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/ui/bikecli.py: Added code to handle prompting the user for
+ whether the method reference should be renamed.
+
+ * bike/refactor/renameMethod.py: Added code to prompt user when
+ type of object instance isn't known
+
+2001-12-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor.py: now takes advantage of typeinfo stuff to
+ deduce types of references
+
+2001-12-03 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/addtypeinfo.py: New classes to deduce the types of
+ references. Adds a typeinfo attribute to each scope ast node,
+ which maps reference names to their types (currently as strings).
+
+2001-11-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added stack to maintain
+ references to object instances while visiting package structure
+
+2001-11-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added support for 'from foo
+ import *'
+
+ * bike/ui/test_bikecli.py: Refactored tests so that all the single
+ file tests are exercised in a package hierarchy context.
+
+2001-11-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Added support to rename methods
+ based on imported classes
+
+2001-11-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/refactor/renameMethod.py: Refactored code (took out
+ RenameMethod class, since wasnt using instance state). Added
+ support for compound references (a.b.c.foo)
+
+2001-11-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: removed. (see load.py instead)
+
+ * bike/refactor/renameMethod.py: Added support for fully qualified
+ classnames in _findClass. e.g. a.b.bah.foo. This means that the ui
+ now supports renaming package nested methods (but not their
+ references).
+
+2001-11-20 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing: Added fixes to parser module to reflect changes in
+ python 2.2 compiler module
+
+2001-11-16 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/output.py: new function 'save' saves an entire ast
+ tree
+
+ * bike/refactor/renameMethod.py: Adapted to take an ast rather
+ than source file
+
+2001-11-14 Phil Dawes <pdawes@bea.com>
+
+ * bike/parsing/load.py: Replacement for load_application.py using
+ Juergen's pythius loader code. load() hierarchically loads a bunch
+ of packages and modules into a big AST.
+
+ * bike/parsing/extended_ast.py: Fleshed out Package and Top nodes
+ to support hierarchical module ast
+
+
+2001-11-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: Fixed import visitor code to
+ handle comma seperated imports. (i.e. import foo,bar). Added code
+ to recognise imports via a from clause (i.e. from foo import bar)
+
+ Removed Imports.py. Integrated code into load_application
+
+
+2001-11-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/load_application.py: Added load_application and
+ Imports modules back into build. Started writing tests for them.
+
+
+2001-11-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * bike/parsing/brmtransformer.py : Rewrote this to override the
+ methods which create nodes at the root of the parse. This means
+ that a few methods adding parser nodelists to ast nodes should
+ cater for practically all the AST nodes.
+
+ * bike/parsing/tokenhandler.py (GetattrTokenHander.renameAttr):
+ added renameAttr method which renames the attribute in the object
+ state, and in the corresponding tokens. This is a very brittle
+ implementation, but will be fixed as new functionality is
+ added. (it's the simplist thing right now)
+
+ * bike/parsing/addtokens.py (AddTokens.visitGetattr): added
+ token support for getattr
+
+ * bike/parsing/tokenutils.py: Added getTokensPreceedingNodes,
+ which takes a parsetree of nodes and returns all the tokens before
+ the node represented by the parsetree. This is important because
+ until there is an token-handling implementation of all the ast
+ nodes, some tokens will be missed. By eating the tokens in between
+ the implemented ast nodes, this ensures all the tokens will be
+ outputted at the end.
+
+
+2001-10-30 Phil Dawes <pdawes@users.sourceforge.net>
+
+ Created new CVS module, which makes use of distutils much more
+ uniform.
+
+ * setup.py: Created distutils setup stript:
+
+2001-10-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Code to handle spacing in inline comments
+
+ * refactor/renameMethod.py: Started code to handle references when
+ object is created in same scope
+
+
+2001-10-24 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: spaces in conditionals (a == b etc..)
+
+ * parsing/tokenutils.py: Refactored code in TokenList to handle
+ rearranging indent/dedent tokens so that whole-line comments are
+ indented with the things they comment. The transformations are now
+ done after the initial scan (slower, but easier to follow)
+
+
+2001-10-23 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: ensured a.b not seperated by spaces.
+
+
+2001-10-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Code to seperate a=b with spaces (a = b)
+
+ * ui/test_cli.py: Merged cli tests with renameMethod tests to
+ ensure all tests work through the cli
+
+2001-10-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * */testall.py: unified testing scripts so that all tests can be
+ run from one place
+
+2001-10-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ui/cli.py: Added simple command line interface.
+
+2001-10-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: Finished simple ast->string functionality
+
+2001-10-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * testall.py: added master test script
+
+ * parsing/tokenhandler.py (FunctionTokenHandler.rename): added
+ rename a function and keep tokens in sync functionality
+
+
+2001-10-12 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/output.py: started tokens -> string functionality
+
+2001-10-07 Phil Dawes <pdawes@users.sourceforge.net>
+ * refactor/renameMethod.py: very basic findClass, findMethod
+ functionality. Needs refactoring into analysis package, and
+ merging with existing code
+
+2001-10-05 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * refactor/simplediff.py: wrote a simple diff utility to support
+ writing top down tests. (i.e. do a renameMethod, then assert that
+ the diff is correct)
+
+
+2001-10-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * refactor/renameMethod.py: Got fedup with trying to secondguess
+ what the parser module should do. Decided to do some top-down
+ proper XP style development. Started with implementing the
+ renameMethod story.
+
+2001-10-01 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/test_addtokens.py: refactored tests so that they use
+ brmtransformer to get a node, rather than mocking it.
+
+2001-09-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/brmtransformer.py: Added nodelist to pass_stmt
+
+2001-09-28 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/addtokens.py (AddTokens.visitModule): finished method
+
+ * parsing/tokenutils.py
+ (TokenConverter.getTokensUptoLastNewlineBeforeCode): method added
+
+2001-09-27 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/addtokens.py: started visitmodule() impl
+
+ * parsing/extended_ast.py: Added tokens support to Module
+
+2001-09-26 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/test_addtokens.py Refactored tests. Made up better names.
+
+2001-09-25 Phil Dawes <pdawes@users.sourceforge.net>
+
+ Laptop broke. Continuing with an older version of the code (hence
+ the gap in changelog)
+
+ * parsing/tokenutils.py (TokenConverter.getTokensToColon): Added
+ function which just gobbles tokens to the next colon. Refactored
+ getTokensStartBlock() to use this instead of walking the nodelist,
+ since I think all
+
+ * parsing/addtokens.py (AddTokens.visitFunction): Now that
+ function and class no longer need to pass in partial nodelists,
+ the code is common. Refactored common code into
+ visitCompoundNode().
+
+ (hence the gap in the changelog)
+
+ * parsing/tokenhandler.py: removed the preceeding tokens code -
+ you ain't gonna need it!
+
+ * parsing/test_addtokens.py: changed visitclass tests to use
+ mockobjects
+
+2001-09-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * parsing/tokenhandler.py:
+
+ * parsing/*: Integrated code with bike parsing package
+
+ Split tokenhandler base classes into seperate module
+ Merged brmast stuff into extended_ast package
+
+
+2001-09-08 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: Added more unit tests
+
+2001-09-06 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: Added more unit tests
+
+
+2001-09-04 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_addtokens.py: Added some mock objects and tests
+
+2001-08-31 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * test_tokenutils.py: beefed up the unit tests a bit
+
+2001-08-29 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * brmtransformer.py: This module now just contains code to add the
+ nodelist member to each ast node as the compiler generates
+ it. This is so the code in addtokens has access to the original
+ python parser nodes to generate tokens from.
+
+ * addtokens.py: Moved the code to add the tokens to the ast nodes
+ to this module. It now relies on there being a 'nodelist' member
+ on each ast object, containing the original python parser
+ nodelist.
+
+ * brmast.py: Added hack to retrofit TokenHandler base class to
+ existing compiler.ast classes. This means that we no longer have
+ to include compiler classes with the distribution.
+ Not sure if this is completely legal - doesn't work with jython.
+
+ Removed stock Python compiler classes from src tree.
+
+
+2001-08-27 Phil Dawes <pdawes@users.sourceforge.net>
+ Set about reimplementing the token stuff - will now add tokens to
+ ast objects *after* the nodes have been processed by the compiler
+ module.
+
+2001-08-22 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Added support for tokenising strings as well as
+ files.
+
+ * test_tokenutils.py: removed testdata directory, and moved all
+ the testdata into this module, now that the tokenutils stuff can
+ take strings as well as files
+
+ * brmtransformer.py: Renamed TransformerWithTokens to Transformer,
+ now that it's in a seperate module.
+
+ * brmast.py: Refactored the added token code out of ast module
+ into this one. Created a seperate mixin called TokenHandler rather
+ than sticking this code in the Node base class. Each class now
+ re-inherits this baseclass.
+ Unfortunately this means that I had to modify the transformer.py
+ file to import brmast rather than ast. Need to think of a better
+ way of doing this.
+
+2001-08-21 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * brmtransformer.py: Created new brmtransformer module and
+ refactored token code from transformer.Transformer into seperate
+ class TransformerWithTokens. Refactored common code from
+ funcdef() and classdef() into do_compound_stmt()
+
+ * transformer.py: Now that all new code is factored out, reverted
+ to original transformer module from Python-2.1/Tools/compiler.
+
+ * tokenutils.py: Added code to alter DEDENT token line numbers
+ when they are rearranged
+
+2001-08-19 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Added code to do token reordering to ensure that
+ comments immediately before a block of code are placed after the
+ previous block DEDENT token.
+
+2001-08-15 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * transformer.py: tidied up code, and released prototype
+
+2001-08-14 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ast.py: Refactored management of tokens into base Node
+ class. Removed code from Class, and moved node->token conversion
+ to transformer.py
+
+ * transformer.py: See above
+
+2001-08-13 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * ast.py: Added support for tokens in 'Class' nodes. 'Class' uses
+ nodes passed to convert to tokens using TokenConverter.
+
+
+2001-08-10 Phil Dawes <pdawes@users.sourceforge.net>
+
+ * tokenutils.py: Started TokenConverter class to convert python
+ parser nodes to tokens.
+
+ * test_tokenutils.py: unit tests for above
+
+------------------ One year later... -------------------------
+
+
+2000-10-21 19:02 gwilder
+
+ * context.py, refactor/.cvsignore, refactor/NewClass.py,
+ refactor/Refactor.py, refactor/__init__.py:
+
+ first refactoring: NewClass
+
+2000-10-21 18:56 gwilder
+
+ * kickstand/: test_HasFromStar.py, test_IsClass.py,
+ test_IsGlobal.py, test_all.py, test_context.py,
+ test_load_application.py, testdata/HasFromStar_tf1.py,
+ testdata/HasFromStar_tf2.py, testdata/IsGlobal_tf1.py,
+ testdata/load_application_tf1.expected,
+ testdata/load_application_tf1.py,
+ testdata/rf_addclass_tf1.expected, testdata/rf_addclass_tf1.py:
+
+ more tests
+
+2000-10-21 18:55 gwilder
+
+ * analysis/: HasFromStar.py, IsClass.py, IsGlobal.py,
+ Superclasses.py, TODO:
+
+ more analysis functions
+
+2000-10-21 18:38 gwilder
+
+ * parsing/: .cvsignore, Imports.py, README, TODO, __init__.py,
+ extended_ast.py, load_application.py:
+
+ parse and load a whole app.
+
+2000-10-13 19:32 gwilder
+
+ * INSTALL, analysis/.cvsignore, analysis/IsClass.py,
+ analysis/IsGlobal.py, analysis/README, analysis/TODO,
+ analysis/__init__.py, assembly/genpy.py, kickstand/test_IsClass.py,
+ kickstand/test_IsGlobal.py, kickstand/test_all.py,
+ kickstand/testdata/.cvsignore, kickstand/testdata/IsClass_tf1.py,
+ kickstand/testdata/IsGlobal_tf1.py:
+
+ new analysis functions; install instructions; testsuite additions.
+
+2000-09-28 00:59 eris
+
+ * .cvsignore, CHANGES, README, assembly/.cvsignore,
+ disassembly/.cvsignore, kickstand/.cvsignore,
+ kickstand/test_common.py, kickstand/treefailtest.py,
+ kickstand/treematchtest.py, kickstand/unequalvars.py,
+ sprocket/.cvsignore, sprocket/common.py, sprocket/common.txt:
+
+
+ added .cvsignore files in each dir got the unit test for
+ sprocket/common.py working, added files for that test.
+
+2000-09-06 01:21 jhermann
+
+ * CHANGES, INSTALL, README, TODO:
+
+ Added administrative files
+
+2000-09-05 22:13 jhermann
+
+ * assembly/__init__.py, disassembly/__init__.py,
+ kickstand/__init__.py, sprocket/__init__.py:
+
+ Made the inits non-empty
+
+2000-09-05 22:11 jhermann
+
+ * __init__.py:
+
+ Minor correction
+
+2000-09-01 18:33 jhermann
+
+ * kickstand/__init__.py, kickstand/test_fsm.py,
+ kickstand/test_regast.py, sprocket/__init__.py, sprocket/common.py,
+ sprocket/fsm.py, sprocket/regast.py:
+
+ Initial source checkin (2000-09-01)
+
+2000-09-01 18:26 jhermann
+
+ * .cvsignore, __init__.py, assembly/__init__.py, assembly/genpy.py,
+ disassembly/__init__.py, disassembly/ast.py, disassembly/consts.py,
+ disassembly/transformer.py:
+
+ Initial source checkin (2000-09-01)
+
+2000-09-01 18:22 jhermann
+
+ * README:
+
+ Additions by shae
+
+2000-08-19 01:35 eris
+
+ * README:
+
+
+ new dir structure by snibril aka Jürgen Herrman first stab at a
+ tree matcher in common.py
+
+2000-08-01 01:44 jhermann
+
+ * README:
+
+ Added a readme dummy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/INSTALL Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,17 @@
+Bicycle Repair Man - a Python Refactoring Browser
+=================================================
+
+$Id: INSTALL,v 1.4 2002/03/29 13:10:49 pdawes Exp $
+
+-----------------------------------------------------------------------------
+
+Bicycle Repair Man requires Python 2.2 and above.
+
+Run
+% python setup.py install
+
+
+Then look for at the relevant README file to integrate Bicycle Repair
+Man with your IDE.
+
+-----------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/NEWS Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,98 @@
+Main highlights of each new version. See ChangeLog for a more detailed
+description of changes.
+
+Version 0.9
+-----------
+
+This version removes the requirement to load files into
+bicyclerepairman before refactoring. Instead, it searches the
+PYTHONPATH (and the path in which the file being queried is in). This
+allows 'findDefinition' queries to be made on references
+(e.g. classes, methods) where the definition is in the python library.
+
+
+Version 0.8
+-----------
+
+This release improves on the internal type-deduction engine to handle
+variables and attributes. To reflect this, 'rename' now works on
+variables and attributes, in addition to methods, functions and
+classes. This release also adds vim support (thanks to Marius Gedminas
+and Matt Yeates for this)
+
+Version 0.7
+-----------
+
+This release includes a totally re-written type querying engine, which
+is much faster and paves the way for new refactorings. It also adds
+the 'FindReferences' and 'FindDefinition' query to emacs and idle.
+
+
+Version 0.6
+-----------
+
+This release adds undo functionality to the mix, and beefs up the idle
+and emacs integration so that code is automatically imported into brm
+when you load a file into a buffer.
+
+
+Version 0.5
+-----------
+
+This release adds the ExtractMethod refactoring
+
+
+Version 0.4
+-----------
+
+This release adds support for IDLE (see README.idle), and fixes a few
+bugs. The CLI and GUI interfaces are now deprecated and have been
+removed from this release.
+
+
+
+Version 0.3
+-----------
+
+This release adds the RenameClass and RenameFunction refactorings.
+It also contains the initial xemacs integration functionality (see
+README.xemacs).
+
+
+
+Version 0.2
+-----------
+
+This release adds a simple GUI for renaming methods - run bikegui.py
+after installation.
+
+There's also some upgrades to pyxmi. It should now be able to generate
+xmi to model all generalizations (including cross-package ones).
+N.B. pyxmi.py is now called py2xmi.py. See changelog for reasons!
+
+
+
+Version 0.1
+-----------
+
+This is the first release of Bicycle Repair Man. It requires python
+version 2.2 and above.
+
+This version supports a partial implementation of the RenameMethod
+refactoring through a command line interface.
+
+It automatically renames the method and references to the method that
+it can deduce. It asks you about method references it can't deduce the
+instance type of.
+
+This software should be considered alpha, and may damage your source
+files - backup your sources before use!
+
+See INSTALL for installation and usage instructions.
+
+
+N.B. This package also contains pyxmi - a little python -> xmi tool I
+cobbled together out of the bicycle repair man parsing package. It
+generates an xmi file out of a source-file or package-structure,
+suitable for loading into the argouml tool. See
+http://argouml.tigris.org.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/PKG-INFO Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: bicyclerepair
+Version: 0.9
+Summary: Bicycle Repair Man, the Python refactoring tool
+Home-page: http://bicyclerepair.sourceforge.net
+Author: Phil Dawes
+Author-email: pdawes@users.sourceforge.net
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,70 @@
+Bicycle Repair Man - a Python Refactoring Browser
+=================================================
+
+Copyright (c) 2000 by Shae Erisson <shae@lapland.fi>
+Copyright (c) 2001-3 by Phil Dawes <pdawes@users.sourceforge.net>
+
+All rights reserved, see COPYING for details.
+
+$Id: README,v 1.7 2003/08/24 19:48:43 pdawes Exp $
+
+-----------------------------------------------------------------------------
+
+Bicycle Repair Man is the Python Refactoring Browser, helping
+Pythonistas everywhere glide over the gory details of refactoring their
+code. Watch him extract jumbled code into well ordered classes. Gasp, as
+he renames all occurrences of a method. Thank You, Bicycle Repair Man!
+
+execute ./testall.py to run all the tests
+
+see INSTALL for installation instructions (uses distutils).
+
+see README.idle, README.emacs etc.. for instructions on how to
+integrate bicyclerepairman into supported IDEs.
+
+-----------------------------------------------------------------------------
+
+
+What's Python?
+
+Python is a programming language. To find out more
+about it, go to the Python Homepage at http://www.python.org/
+
+
+What's a Refactoring Browser?
+
+A Refactoring Browser is an editor that automates Refactorings. The
+first Refactoring Browser was written by Dr. Don Roberts and Dr. John
+Brant at the University of Illinois in Urbana-Champagne. Dr. Don
+Roberts wrote his Ph.D. thesis on the design and implementation of the
+Refactoring Browser. For more detail, read the aforementioned thesis
+at http://st-www.cs.uiuc.edu/~droberts/thesis.pdf
+
+
+What's a Refactoring?
+
+A Refactoring is a behaviour preserving change to source code. Some
+Refactorings are RenameVariable, RenameClass, RenameMethod,
+PullUpMethod, and PushDownVariable. Lots of people say it's very easy
+to just type a different name in where your class, method, or variable
+is defined. That's not always a refactoring though. The Refactoring
+Browser is smart enough to rename every reference to your class,
+method or variable. If you've ever renamed a variable and broken
+classes in widely scattered parts of your system, you might be happier
+using a Refactoring Browser. A Refactoring Browser operates on any of
+method, function, class, or variable. It can add, delete, rename, move
+up down or sideways, inline and abstract. There are some operations
+that are specific to one of the three types, such as abstracting a
+variable into accessors, or turing several lines of code into a
+separate method.
+
+For more information on Refactoring, check out the websites of Martin
+Fowler at http://www.martinfowler.com/ and his Refactoring Site at
+http://www.refactoring.com/ .
+
+
+Why Bicycle Repair Man?
+
+The Bicycle Repair Man was a superhero in a Monty Python skit, his
+special power was repairing bicycles.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.emacs Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,115 @@
+Instructions for running Bicycle Repair Man through emacs/xemacs
+----------------------------------------------------------------
+
+N.B. You need xemacs / emacs 21 or above.
+
+The emacs integration utilises the excellent 'Pymacs' package, written
+by François Pinard, which integrates python with emacs. A copy of this
+software is included with this package.
+
+
+There are 3 steps to installing bicyclerepairman for emacs:
+
+1) Install the base bicyclerepairman package
+
+2) Install the Pymacs package (if you haven't already got it)
+
+3) Modify your .emacs to active the bicyclerepairman functionality
+
+See the sections below for instructions on doing each of these.
+
+WINDOWS USERS:
+You need to have both the python executable and the scripts directory
+(e.g. c:\Python22/Scripts) in your path for Bicyclerepairman to work.
+
+There are a couple of niggles with brm/emacs on
+windows. See the comments at the end of this file.
+
+
+
+
+
+
+
+1) Installation of Base Bicyclerepairman:
+-----------------------------------------
+
+- install bicyclerepair man as per INSTALL
+
+
+2) Installation of Pymacs:
+--------------------------
+
+(You can skip this if you already have pymacs installed.
+
+- Go to the ide-integration/Pymacs-0.20 directory
+- Run
+ python setup.py install
+- Run
+ python setup-emacs.py -l <LISP DIR>
+ OR
+ python setup-emacs.py -E xemacs -l <LISP DIR>
+ Depending on your version of emacs.
+
+- Add the following into your .emacs or .xemacs/init.el:
+
+;; pymacs
+(autoload 'pymacs-load "pymacs" nil t)
+(autoload 'pymacs-eval "pymacs" nil t)
+(autoload 'pymacs-apply "pymacs")
+(autoload 'pymacs-call "pymacs")
+
+- Check that it has installed correctly:
+ (Taken from the pymacs README)
+
+
+ To check that `pymacs.el' is properly installed, start Emacs and give
+ it the command `M-x load-library RET pymacs': you should not receive
+ any error.
+
+ To check that `pymacs.py' is properly installed, start
+ an interactive Python session (e.g. from a command shell) and type
+ `from Pymacs import lisp': you should not receive any error.
+
+ To check that `pymacs-services' is properly installed, type
+ `pymacs-services' in a shell; you should then get a line ending
+ with "(pymacs-version VERSION)". Press ctrl-c to exit.
+
+
+If you have any problems, consult the README file included with the
+pymacs distribution. N.B. I renamed the setup script to setup-emacs.py
+to make it more intuitive and easier for windows users. I've also
+added a pymacs-services.bat file to allow it to run on windows.
+
+
+3) Activating the Bike/Emacs integration
+----------------------------------------
+
+Add the following to your .emacs or .xemacs/init.el, after the pymacs
+stuff:
+
+(pymacs-load "bikeemacs" "brm-")
+(brm-init)
+
+
+You need to be using python-mode for the bicyclerepairman menu to
+appear. If you haven't already, enable this with:
+
+(autoload 'python-mode "python-mode" "Python editing mode." t)
+(setq auto-mode-alist
+ (cons '("\\.py$" . python-mode) auto-mode-alist))
+
+
+
+Usage:
+------
+
+Load a python file into emacs. A BicycleRepairMan menu should appear.
+
+
+Windows GNU-Emacs users
+-----------------------
+
+The load dialog in windows GNU-Emacs doesn't seem to allow selection
+of directories. If this is the case for you, use 'M-x brm-load' to
+import a package hierarchy.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.idle Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,47 @@
+BicycleRepairMan_Idle.py is the name of the idle integration module.
+
+
+Installation on Python 2.3 or idlefork
+--------------------------------------
+1) Install Bicycle Repair Man (see INSTALL)
+
+2) Add the following to your ~/.idlerc/config-extensions.cfg:
+(NOTE - I had to put this in my
+<PYTHONLIBDIR>/idlelib/config-extensions.def for it to work correctly)
+
+[BicycleRepairMan_Idle]
+enable=1
+trace=0
+[BicycleRepairMan_Idle_cfgBindings]
+brm-find-references=
+brm-find-definition=
+brm-rename=
+brm-extract-method=
+brm-undo=
+
+Python sourcefile editor windows will now have a 'BicycleRepairMan'
+menu.
+
+
+
+Installation on python 2.2 version of idle
+------------------------------------------
+
+1) Install Bicycle Repair Man (see INSTALL)
+
+2) Add the following line to your <idle-installation>/config.txt or
+~/.idle:
+
+[BicycleRepairMan_Idle]
+
+
+Python sourcefile editor windows will now have a 'BicycleRepairMan'
+menu.
+
+
+
+Caveats
+-------
+
+Sometimes dialogs get lost behind windows. If things seem to have
+paused in a rename-method, try looking for a dialog.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/README.vim Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,2 @@
+Instructions for installing bike.vim are in the file itself.
+See ide-integration/bike.vim
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+# The root bicyclerepairman package
+# do:
+# ---------------------
+# import bike
+# ctx = bike.load()
+# ---------------------
+# to instantiate a bicyclerepairman context object
+
+
+from bikefacade import init, NotAPythonModuleOrPackageException, CouldntLocateASTNodeFromCoordinatesException, UndoStackEmptyException
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/bikefacade.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,274 @@
+import os
+import sys
+import compiler
+from parser import ParserError
+from bike.parsing.pathutils import getRootDirectory
+from bike.refactor import extractMethod
+from bike.refactor.rename import rename
+from bike.refactor.extractMethod import coords
+from bike.transformer.save import save as saveUpdates
+from bike.parsing.utils import fqn_rcar, fqn_rcdr
+from bike.parsing import visitor
+from bike.transformer.undo import getUndoStack, UndoStackEmptyException
+from bike.parsing.fastparserast import getRoot, Class, Function
+from bike.query.common import getScopeForLine
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType
+from bike.query.findReferences import findReferences
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.refactor import inlineVariable, extractVariable, moveToModule
+from bike.parsing.load import Cache
+from bike import log
+
+def init():
+ #context = BRMContext_impl()
+ context = BRMContext_wrapper()
+ return context
+
+# the context object public interface
+class BRMContext(object):
+
+
+ def save(self):
+ """ save the changed files out to disk """
+
+ def setRenameMethodPromptCallback(self, callback):
+ """
+ sets a callback to ask the user about method refs which brm
+ can't deduce the type of. The callback must be callable, and
+ take the following parameters:
+ - filename
+ - linenumber
+ - begin column
+ - end column
+ (begin and end columns enclose the problematic method call)
+ """
+
+ def renameByCoordinates(self, filename_path, line, col, newname):
+ """ an ide friendly method which renames a class/fn/method
+ pointed to by the coords and filename"""
+
+ def extract(self, filename_path,
+ begin_line, begin_col,
+ end_line, end_col,
+ name):
+ """ extracts the region into the named method/function based
+ on context"""
+
+ def inlineLocalVariable(self,filename_path, line, col):
+ """ Inlines the variable pointed to by
+ line:col. (N.B. line:col can also point to a reference to the
+ variable as well as the definition) """
+
+
+ def extractLocalVariable(self,filename_path, begin_line, begin_col,
+ end_line, end_col, variablename):
+ """ Extracts the region into a variable """
+
+ def setProgressLogger(self,logger):
+ """ Sets the progress logger to an object with a write method
+ """
+
+ def setWarningLogger(self,logger):
+ """ Sets the warning logger to an object with a write method
+ """
+
+ def undo(self):
+ """ undoes the last refactoring. WARNING: this is dangerous if
+ the user has modified files since the last refactoring.
+ Raises UndoStackEmptyException"""
+
+ def findReferencesByCoordinates(self, filename_path, line, column):
+ """ given the coords of a function, class, method or variable
+ returns a generator which finds references to it.
+ """
+
+ def findDefinitionByCoordinates(self,filename_path,line,col):
+ """ given the coordates to a reference, tries to find the
+ definition of that reference """
+
+ def moveClassToNewModule(self,filename_path, line,
+ newfilename):
+ """ moves the class pointed to by (filename_path, line)
+ to a new module """
+
+
+class NotAPythonModuleOrPackageException: pass
+class CouldntLocateASTNodeFromCoordinatesException: pass
+
+
+# Wrapper to ensure that caches are purged on each request
+class BRMContext_wrapper:
+ def __init__(self):
+ self.brmctx = BRMContext_impl()
+
+ def __getattr__(self,name):
+ return BRMContext_callWrapper(self.brmctx,name)
+
+
+class BRMContext_callWrapper:
+ def __init__(self,brmctx,methodname):
+ self.name = methodname
+ self.brmctx = brmctx
+
+ def __call__(self,*args):
+ Cache.instance.reset()
+ try:
+ return getattr(self.brmctx,self.name)(*args)
+ finally:
+ Cache.instance.reset()
+
+
+class BRMContext_impl(BRMContext):
+
+ def __init__(self):
+ self.ast = getRoot()
+
+ # Used because some refactorings delegate back to the user.
+ # this flag ensures that code isnt imported during those times
+ self.readyToLoadNewCode = 1
+ self.paths = []
+ getUndoStack(1) # force new undo stack
+ if not getRoot().unittestmode:
+ log.warning = sys.stderr
+ self.promptUserClientCallback = None
+
+ def _getAST(self):
+ return self.ast
+
+ # returns a list of saved filenames
+ def save(self):
+ savedfiles = saveUpdates()
+ return savedfiles
+
+ def setRenameMethodPromptCallback(self, callback):
+ self.promptUserClientCallback = callback
+
+
+ def normalizeFilename(self,filename):
+ filename = os.path.expanduser(filename)
+ filename = os.path.normpath(os.path.abspath(filename))
+ return filename
+
+ def extractMethod(self, filename_path,
+ begin_line, begin_column,
+ end_line, end_column,
+ methodname):
+ self.extract(filename_path, begin_line, begin_column,
+ end_line, end_column,methodname)
+
+ def extractFunction(self, filename_path,
+ begin_line, begin_column,
+ end_line, end_column,
+ methodname):
+ self.extract(filename_path, begin_line, begin_column,
+ end_line, end_column,methodname)
+
+ # does it based on context
+ def extract(self, filename_path,
+ begin_line, begin_col,
+ end_line, end_col,
+ name):
+ filename_path = self.normalizeFilename(filename_path)
+ extractMethod.extractMethod(filename_path,
+ coords(begin_line, begin_col),
+ coords(end_line, end_col), name)
+
+ def inlineLocalVariable(self,filename_path, line, col):
+ filename_path = self.normalizeFilename(filename_path)
+ inlineVariable.inlineLocalVariable(filename_path,line,col)
+
+ def extractLocalVariable(self,filename_path, begin_line, begin_col,
+ end_line, end_col, variablename):
+ filename_path = self.normalizeFilename(filename_path)
+ extractVariable.extractLocalVariable(filename_path,
+ coords(begin_line, begin_col),
+ coords(end_line, end_col),
+ variablename)
+
+ def moveClassToNewModule(self,filename_path, line,
+ newfilename):
+ filename_path = self.normalizeFilename(filename_path)
+ newfilename = self.normalizeFilename(newfilename)
+ moveToModule.moveClassToNewModule(filename_path, line,
+ newfilename)
+
+ def undo(self):
+ getUndoStack().undo()
+
+ def _promptUser(self, filename, lineno, colbegin, colend):
+ return self.promptUserClientCallback(filename, lineno, colbegin, colend)
+
+
+ # must be an object with a write method
+ def setProgressLogger(self,logger):
+ log.progress = logger
+
+ # must be an object with a write method
+ def setWarningLogger(self,logger):
+ log.warning = logger
+
+
+ # filename_path must be absolute
+ def renameByCoordinates(self, filename_path, line, col, newname):
+ filename_path = self.normalizeFilename(filename_path)
+ Cache.instance.reset()
+ try:
+ self._setNonLibPythonPath(filename_path)
+ rename(filename_path,line,col,newname,
+ self.promptUserClientCallback)
+ finally:
+ Cache.instance.reset()
+
+ def _reverseCoordsIfWrongWayRound(self, colbegin, colend):
+ if(colbegin > colend):
+ colbegin,colend = colend,colbegin
+ return colbegin,colend
+
+
+ def findDefinitionByCoordinates(self,filename_path,line,col):
+ filename_path = self.normalizeFilename(filename_path)
+ self._setCompletePythonPath(filename_path)
+ return findAllPossibleDefinitionsByCoords(filename_path,line,col)
+
+
+ # filename_path must be absolute
+ def findReferencesByCoordinates(self, filename_path, line, column):
+ filename_path = self.normalizeFilename(filename_path)
+ self._setNonLibPythonPath(filename_path)
+ return findReferences(filename_path,line,column)
+
+ def refreshASTFromFileSystem(self):
+ for path in self.paths:
+ self.ast = loadast(path, self.ast)
+
+ def _setCompletePythonPath(self,filename):
+ pythonpath = [] + sys.path # make a copy
+ self.ast.pythonpath = pythonpath
+
+ def _setNonLibPythonPath(self,filename):
+ if getRoot().unittestmode:
+ return
+ pythonpath = self._removeLibdirsFromPath(sys.path)
+ pythonpath = [os.path.abspath(p) for p in pythonpath]
+ self.ast.pythonpath = pythonpath
+
+ def _getCurrentSearchPath(self):
+ return self.ast.pythonpath
+
+ def _removeLibdirsFromPath(self, pythonpath):
+ libdir = os.path.join(sys.prefix,"lib").lower()
+ pythonpath = [p for p in pythonpath
+ if not p.lower().startswith(libdir)]
+ return pythonpath
+
+
+def _deducePackageOfFile(filename):
+ package = ""
+ dot = ""
+ dir = os.path.dirname(filename)
+ while dir != ""and \
+ os.path.exists(os.path.join(dir, "__init__.py")):
+ dir, dirname = os.path.split(dir)
+ package = dirname+dot+package
+ dot = "."
+ return package
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/globals.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,6 @@
+try:
+ True = 1
+ False = 0
+except:
+ pass
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/log.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,8 @@
+import sys
+class SilentLogger:
+ def write(*args):
+ pass
+
+progress = SilentLogger()
+warning = SilentLogger()
+#warning = sys.stderr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/logging.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1995 @@
+#! /usr/bin/env python
+#
+# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that copyright notice and this permission notice appear in
+# supporting documentation, and that the name of Vinay Sajip
+# not be used in advertising or publicity pertaining to distribution
+# of the software without specific, written prior permission.
+# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# For the change history, see README.txt in the distribution.
+#
+# This file is part of the Python logging distribution. See
+# http://www.red-dove.com/python_logging.html
+#
+
+"""
+Logging module for Python. Based on PEP 282 and comments thereto in
+comp.lang.python, and influenced by Apache's log4j system.
+
+Should work under Python versions >= 1.5.2, except that source line
+information is not available unless 'inspect' is.
+
+Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
+
+To use, simply 'import logging' and log away!
+"""
+
+import sys, os, types, time, string, socket, cPickle, cStringIO
+
+from SocketServer import ThreadingTCPServer, StreamRequestHandler
+
+
+try:
+ import thread
+except ImportError:
+ thread = None
+try:
+ import inspect
+except ImportError:
+ inspect = None
+
+__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
+__status__ = "alpha"
+__version__ = "0.4.5"
+__date__ = "4 June 2002"
+
+#---------------------------------------------------------------------------
+# Miscellaneous module data
+#---------------------------------------------------------------------------
+
+#
+#_srcfile is used when walking the stack to check when we've got the first
+# caller stack frame.
+#If run as a script, __file__ is not bound.
+#
+if __name__ == "__main__":
+ _srcfile = None
+else:
+ if string.lower(__file__[-4:]) in ['.pyc', '.pyo']:
+ _srcfile = __file__[:-4] + '.py'
+ else:
+ _srcfile = __file__
+ _srcfile = os.path.normcase(_srcfile)
+
+#
+#_startTime is used as the base when calculating the relative time of events
+#
+_startTime = time.time()
+
+#
+# Some constants...
+#
+
+DEFAULT_TCP_LOGGING_PORT = 9020
+DEFAULT_UDP_LOGGING_PORT = 9021
+DEFAULT_HTTP_LOGGING_PORT = 9022
+DEFAULT_SOAP_LOGGING_PORT = 9023
+DEFAULT_LOGGING_CONFIG_PORT = 9030
+SYSLOG_UDP_PORT = 514
+
+#---------------------------------------------------------------------------
+# Level related stuff
+#---------------------------------------------------------------------------
+#
+# Default levels and level names, these can be replaced with any positive set
+# of values having corresponding names. There is a pseudo-level, ALL, which
+# is only really there as a lower limit for user-defined levels. Handlers and
+# loggers are initialized with ALL so that they will log all messages, even
+# at user-defined levels.
+#
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARN = 30
+INFO = 20
+DEBUG = 10
+ALL = 0
+
+_levelNames = {
+ CRITICAL : 'CRITICAL',
+ ERROR : 'ERROR',
+ WARN : 'WARN',
+ INFO : 'INFO',
+ DEBUG : 'DEBUG',
+ ALL : 'ALL',
+ 'CRITICAL' : CRITICAL,
+ 'ERROR' : ERROR,
+ 'WARN' : WARN,
+ 'INFO' : INFO,
+ 'DEBUG' : DEBUG,
+ 'ALL' : ALL,
+}
+
+def getLevelName(lvl):
+ """
+ Return the textual representation of logging level 'lvl'. If the level is
+ one of the predefined levels (CRITICAL, ERROR, WARN, INFO, DEBUG) then you
+ get the corresponding string. If you have associated levels with names
+ using addLevelName then the name you have associated with 'lvl' is
+ returned. Otherwise, the string "Level %s" % lvl is returned.
+ """
+ return _levelNames.get(lvl, ("Level %s" % lvl))
+
+def addLevelName(lvl, levelName):
+ """
+ Associate 'levelName' with 'lvl'. This is used when converting levels
+ to text during message formatting.
+ """
+ _acquireLock()
+ try: #unlikely to cause an exception, but you never know...
+ _levelNames[lvl] = levelName
+ _levelNames[levelName] = lvl
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Thread-related stuff
+#---------------------------------------------------------------------------
+
+#
+#_lock is used to serialize access to shared data structures in this module.
+#This needs to be an RLock because fileConfig() creates Handlers and so
+#might arbitrary user threads. Since Handler.__init__() updates the shared
+#dictionary _handlers, it needs to acquire the lock. But if configuring,
+#the lock would already have been acquired - so we need an RLock.
+#The same argument applies to Loggers and Manager.loggerDict.
+#
+_lock = None
+
+def _acquireLock():
+ """
+ Acquire the module-level lock for serializing access to shared data.
+ This should be released with _releaseLock().
+ """
+ global _lock
+ if (not _lock) and thread:
+ import threading #this had better work
+ _lock = threading.RLock()
+ if _lock:
+ _lock.acquire()
+
+def _releaseLock():
+ """
+ Release the module-level lock acquired by calling _acquireLock().
+ """
+ if _lock:
+ _lock.release()
+
+#---------------------------------------------------------------------------
+# The logging record
+#---------------------------------------------------------------------------
+
+class LogRecord:
+ """
+ LogRecord instances are created every time something is logged. They
+ contain all the information pertinent to the event being logged. The
+ main information passed in is in msg and args, which are combined
+ using msg % args to create the message field of the record. The record
+ also includes information such as when the record was created, the
+ source line where the logging call was made, and any exception
+ information to be logged.
+ """
+ def __init__(self, name, lvl, pathname, lineno, msg, args, exc_info):
+ """
+ Initialize a logging record with interesting information.
+ """
+ ct = time.time()
+ self.name = name
+ self.msg = msg
+ self.args = args
+ self.levelname = getLevelName(lvl)
+ self.levelno = lvl
+ self.pathname = pathname
+ try:
+ self.filename = os.path.basename(pathname)
+ self.module = os.path.splitext(self.filename)[0]
+ except:
+ self.filename = pathname
+ self.module = "Unknown module"
+ self.exc_info = exc_info
+ self.lineno = lineno
+ self.created = ct
+ self.msecs = (ct - long(ct)) * 1000
+ self.relativeCreated = (self.created - _startTime) * 1000
+ if thread:
+ self.thread = thread.get_ident()
+ else:
+ self.thread = None
+
+ def __str__(self):
+ return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,
+ self.pathname, self.lineno, self.msg)
+
+ def getMessage(self):
+ """
+ Return the message for this LogRecord, merging any user-supplied
+ arguments with the message.
+ """
+ msg = str(self.msg)
+ if self.args:
+ msg = msg % self.args
+ return msg
+
+#---------------------------------------------------------------------------
+# Formatter classes and functions
+#---------------------------------------------------------------------------
+
+class Formatter:
+ """
+ Formatters need to know how a LogRecord is constructed. They are
+ responsible for converting a LogRecord to (usually) a string which can
+ be interpreted by either a human or an external system. The base Formatter
+ allows a formatting string to be specified. If none is supplied, the
+ default value of "%s(message)\\n" is used.
+
+ The Formatter can be initialized with a format string which makes use of
+ knowledge of the LogRecord attributes - e.g. the default value mentioned
+ above makes use of the fact that the user's message and arguments are pre-
+ formatted into a LogRecord's message attribute. Currently, the useful
+ attributes in a LogRecord are described by:
+
+ %(name)s Name of the logger (logging channel)
+ %(levelno)s Numeric logging level for the message (DEBUG, INFO,
+ WARN, ERROR, CRITICAL)
+ %(levelname)s Text logging level for the message ("DEBUG", "INFO",
+ "WARN", "ERROR", "CRITICAL")
+ %(pathname)s Full pathname of the source file where the logging
+ call was issued (if available)
+ %(filename)s Filename portion of pathname
+ %(module)s Module (name portion of filename)
+ %(lineno)d Source line number where the logging call was issued
+ (if available)
+ %(created)f Time when the LogRecord was created (time.time()
+ return value)
+ %(asctime)s Textual time when the LogRecord was created
+ %(msecs)d Millisecond portion of the creation time
+ %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+ relative to the time the logging module was loaded
+ (typically at application startup time)
+ %(thread)d Thread ID (if available)
+ %(message)s The result of msg % args, computed just as the
+ record is emitted
+ """
+ def __init__(self, fmt=None, datefmt=None):
+ """
+ Initialize the formatter either with the specified format string, or a
+ default as described above. Allow for specialized date formatting with
+ the optional datefmt argument (if omitted, you get the ISO8601 format).
+ """
+ if fmt:
+ self._fmt = fmt
+ else:
+ self._fmt = "%(message)s"
+ self.datefmt = datefmt
+
+ def formatTime(self, record, datefmt=None):
+ """
+ This method should be called from format() by a formatter which
+ wants to make use of a formatted time. This method can be overridden
+ in formatters to provide for any specific requirement, but the
+ basic behaviour is as follows: if datefmt (a string) is specified,
+ it is used with time.strftime() to format the creation time of the
+ record. Otherwise, the ISO8601 format is used. The resulting
+ string is returned.
+ """
+ ct = record.created
+ if datefmt:
+ s = time.strftime(datefmt, time.localtime(ct))
+ else:
+ t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ct))
+ s = "%s,%03d" % (t, record.msecs)
+ return s
+
+ def formatException(self, ei):
+ """
+ Format the specified exception information as a string. This
+ default implementation just uses traceback.print_exception()
+ """
+ import traceback
+ sio = cStringIO.StringIO()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
+ s = sio.getvalue()
+ sio.close()
+ if s[-1] == "\n":
+ s = s[:-1]
+ return s
+
+ def format(self, record):
+ """
+ The record's attribute dictionary is used as the operand to a
+ string formatting operation which yields the returned string.
+ Before formatting the dictionary, a couple of preparatory steps
+ are carried out. The message attribute of the record is computed
+ using msg % args. If the formatting string contains "%(asctime)",
+ formatTime() is called to format the event time. If there is
+ exception information, it is formatted using formatException()
+ and appended to the message.
+ """
+ record.message = record.getMessage()
+ if string.find(self._fmt,"%(asctime)") >= 0:
+ record.asctime = self.formatTime(record, self.datefmt)
+ s = self._fmt % record.__dict__
+ if record.exc_info:
+ if s[-1] != "\n":
+ s = s + "\n"
+ s = s + self.formatException(record.exc_info)
+ return s
+
+#
+# The default formatter to use when no other is specified
+#
+_defaultFormatter = Formatter()
+
+class BufferingFormatter:
+ """
+ A formatter suitable for formatting a number of records.
+ """
+ def __init__(self, linefmt=None):
+ """
+ Optionally specify a formatter which will be used to format each
+ individual record.
+ """
+ if linefmt:
+ self.linefmt = linefmt
+ else:
+ self.linefmt = _defaultFormatter
+
+ def formatHeader(self, records):
+ """
+ Return the header string for the specified records.
+ """
+ return ""
+
+ def formatFooter(self, records):
+ """
+ Return the footer string for the specified records.
+ """
+ return ""
+
+ def format(self, records):
+ """
+ Format the specified records and return the result as a string.
+ """
+ rv = ""
+ if len(records) > 0:
+ rv = rv + self.formatHeader(records)
+ for record in records:
+ rv = rv + self.linefmt.format(record)
+ rv = rv + self.formatFooter(records)
+ return rv
+
+#---------------------------------------------------------------------------
+# Filter classes and functions
+#---------------------------------------------------------------------------
+
+class Filter:
+ """
+ The base filter class. Loggers and Handlers can optionally use Filter
+ instances to filter records as desired. The base filter class only allows
+ events which are below a certain point in the logger hierarchy. For
+ example, a filter initialized with "A.B" will allow events logged by
+ loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B"
+ etc. If initialized with the empty string, all events are passed.
+ """
+ def __init__(self, name=''):
+ """
+ Initialize with the name of the logger which, together with its
+ children, will have its events allowed through the filter. If no
+ name is specified, allow every event.
+ """
+ self.name = name
+ self.nlen = len(name)
+
+ def filter(self, record):
+ """
+ Is the specified record to be logged? Returns 0 for no, nonzero for
+ yes. If deemed appropriate, the record may be modified in-place.
+ """
+ if self.nlen == 0:
+ return 1
+ elif self.name == record.name:
+ return 1
+ elif string.find(record.name, self.name, 0, self.nlen) != 0:
+ return 0
+ return (record.name[self.nlen] == ".")
+
+class Filterer:
+ """
+ A base class for loggers and handlers which allows them to share
+ common code.
+ """
+ def __init__(self):
+ """
+ Initialize the list of filters to be an empty list.
+ """
+ self.filters = []
+
+ def addFilter(self, filter):
+ """
+ Add the specified filter to this handler.
+ """
+ if not (filter in self.filters):
+ self.filters.append(filter)
+
+ def removeFilter(self, filter):
+ """
+ Remove the specified filter from this handler.
+ """
+ if filter in self.filters:
+ self.filters.remove(filter)
+
+ def filter(self, record):
+ """
+ Determine if a record is loggable by consulting all the filters. The
+ default is to allow the record to be logged; any filter can veto this
+ and the record is then dropped. Returns a boolean value.
+ """
+ rv = 1
+ for f in self.filters:
+ if not f.filter(record):
+ rv = 0
+ break
+ return rv
+
+#---------------------------------------------------------------------------
+# Handler classes and functions
+#---------------------------------------------------------------------------
+
+_handlers = {} #repository of handlers (for flushing when shutdown called)
+
+class Handler(Filterer):
+ """
+ The base handler class. Acts as a placeholder which defines the Handler
+ interface. Handlers can optionally use Formatter instances to format
+ records as desired. By default, no formatter is specified; in this case,
+ the 'raw' message as determined by record.message is logged.
+ """
+ def __init__(self, level=ALL):
+ """
+ Initializes the instance - basically setting the formatter to None
+ and the filter list to empty.
+ """
+ Filterer.__init__(self)
+ self.level = level
+ self.formatter = None
+ #get the module data lock, as we're updating a shared structure.
+ _acquireLock()
+ try: #unlikely to raise an exception, but you never know...
+ _handlers[self] = 1
+ finally:
+ _releaseLock()
+ self.createLock()
+
+ def createLock(self):
+ """
+ Acquire a thread lock for serializing access to the underlying I/O.
+ """
+ if thread:
+ self.lock = thread.allocate_lock()
+ else:
+ self.lock = None
+
+ def acquire(self):
+ """
+ Acquire the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.acquire()
+
+ def release(self):
+ """
+ Release the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.release()
+
+ def setLevel(self, lvl):
+ """
+ Set the logging level of this handler.
+ """
+ self.level = lvl
+
+ def format(self, record):
+ """
+ Do formatting for a record - if a formatter is set, use it.
+ Otherwise, use the default formatter for the module.
+ """
+ if self.formatter:
+ fmt = self.formatter
+ else:
+ fmt = _defaultFormatter
+ return fmt.format(record)
+
+ def emit(self, record):
+ """
+ Do whatever it takes to actually log the specified logging record.
+ This version is intended to be implemented by subclasses and so
+ raises a NotImplementedError.
+ """
+ raise NotImplementedError, 'emit must be implemented '\
+ 'by Handler subclasses'
+
+ def handle(self, record):
+ """
+ Conditionally emit the specified logging record, depending on
+ filters which may have been added to the handler. Wrap the actual
+ emission of the record with acquisition/release of the I/O thread
+ lock.
+ """
+ if self.filter(record):
+ self.acquire()
+ try:
+ self.emit(record)
+ finally:
+ self.release()
+
+ def setFormatter(self, fmt):
+ """
+ Set the formatter for this handler.
+ """
+ self.formatter = fmt
+
+ def flush(self):
+ """
+ Ensure all logging output has been flushed. This version does
+ nothing and is intended to be implemented by subclasses.
+ """
+ pass
+
+ def close(self):
+ """
+ Tidy up any resources used by the handler. This version does
+ nothing and is intended to be implemented by subclasses.
+ """
+ pass
+
+ def handleError(self):
+ """
+ This method should be called from handlers when an exception is
+ encountered during an emit() call. By default it does nothing,
+ which means that exceptions get silently ignored. This is what is
+ mostly wanted for a logging system - most users will not care
+ about errors in the logging system, they are more interested in
+ application errors. You could, however, replace this with a custom
+ handler if you wish.
+ """
+ #import traceback
+ #ei = sys.exc_info()
+ #traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
+ #del ei
+ pass
+
+class StreamHandler(Handler):
+ """
+ A handler class which writes logging records, appropriately formatted,
+ to a stream. Note that this class does not close the stream, as
+ sys.stdout or sys.stderr may be used.
+ """
+ def __init__(self, strm=None):
+ """
+ If strm is not specified, sys.stderr is used.
+ """
+ Handler.__init__(self)
+ if not strm:
+ strm = sys.stderr
+ self.stream = strm
+ self.formatter = None
+
+ def flush(self):
+ """
+ Flushes the stream.
+ """
+ self.stream.flush()
+
+ def emit(self, record):
+ """
+ If a formatter is specified, it is used to format the record.
+ The record is then written to the stream with a trailing newline
+ [N.B. this may be removed depending on feedback]. If exception
+ information is present, it is formatted using
+ traceback.print_exception and appended to the stream.
+ """
+ try:
+ msg = self.format(record)
+ self.stream.write("%s\n" % msg)
+ self.flush()
+ except:
+ self.handleError()
+
+class FileHandler(StreamHandler):
+ """
+ A handler class which writes formatted logging records to disk files.
+ """
+ def __init__(self, filename, mode="a+"):
+ """
+ Open the specified file and use it as the stream for logging.
+ By default, the file grows indefinitely. You can call setRollover()
+ to allow the file to rollover at a predetermined size.
+ """
+ StreamHandler.__init__(self, open(filename, mode))
+ self.maxBytes = 0
+ self.backupCount = 0
+ self.baseFilename = filename
+ #self.backupIndex = 0
+ self.mode = mode
+
+ def setRollover(self, maxBytes, backupCount):
+ """
+ Set the rollover parameters so that rollover occurs whenever the
+ current log file is nearly maxBytes in length. If backupCount
+ is >= 1, the system will successively create new files with the
+ same pathname as the base file, but with extensions ".1", ".2"
+ etc. appended to it. For example, with a backupCount of 5 and a
+ base file name of "app.log", you would get "app.log", "app.log.1",
+ "app.log.2", ... through to "app.log.5". When the last file reaches
+ its size limit, the logging reverts to "app.log" which is truncated
+ to zero length. If maxBytes is zero, rollover never occurs.
+ """
+ self.maxBytes = maxBytes
+ self.backupCount = backupCount
+ if maxBytes > 0:
+ self.mode = "a+"
+
+ def doRollover(self):
+ """
+ Do a rollover, as described in setRollover().
+ """
+# Old algorithm
+# if self.backupIndex >= self.backupCount:
+# self.backupIndex = 0
+# fn = self.baseFilename
+# else:
+# self.backupIndex = self.backupIndex + 1
+# fn = "%s.%d" % (self.baseFilename, self.backupIndex)
+# self.stream.close()
+# self.stream = open(fn, "w+")
+ self.stream.close()
+ if self.backupCount > 0:
+ for i in range(self.backupCount - 1, 0, -1):
+ sfn = "%s.%d" % (self.baseFilename, i)
+ dfn = "%s.%d" % (self.baseFilename, i + 1)
+ if os.path.exists(sfn):
+ #print "%s -> %s" % (sfn, dfn)
+ if os.path.exists(dfn):
+ os.remove(dfn)
+ os.rename(sfn, dfn)
+ dfn = self.baseFilename + ".1"
+ if os.path.exists(dfn):
+ os.remove(dfn)
+ os.rename(self.baseFilename, dfn)
+ self.stream = open(self.baseFilename, "w+")
+
+ def emit(self, record):
+ """
+ Output the record to the file, catering for rollover as described
+ in setRollover().
+ """
+ if self.maxBytes > 0: # are we rolling over?
+ msg = "%s\n" % self.format(record)
+ if self.stream.tell() + len(msg) >= self.maxBytes:
+ self.doRollover()
+ StreamHandler.emit(self, record)
+
+ def close(self):
+ """
+ Closes the stream.
+ """
+ self.stream.close()
+
+class SocketHandler(Handler):
+ """
+ A handler class which writes logging records, in pickle format, to
+ a streaming socket. The socket is kept open across logging calls.
+ If the peer resets it, an attempt is made to reconnect on the next call.
+ Note that the very simple wire protocol used means that packet sizes
+ are expected to be encodable within 16 bits (i.e. < 32767 bytes).
+ """
+
+ def __init__(self, host, port):
+ """
+ Initializes the handler with a specific host address and port.
+ """
+ Handler.__init__(self)
+ self.host = host
+ self.port = port
+ self.sock = None
+ self.closeOnError = 1
+
+ def makeSocket(self):
+ """
+ A factory method which allows subclasses to define the precise
+ type of socket they want.
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((self.host, self.port))
+ return s
+
+ def send(self, s):
+ """
+ Send a pickled string to the socket. This function allows for
+ partial sends which can happen when the network is busy.
+ """
+ sentsofar = 0
+ left = len(s)
+ while left > 0:
+ sent = self.sock.send(s[sentsofar:])
+ sentsofar = sentsofar + sent
+ left = left - sent
+
+ def makePickle(self, record):
+ """
+ Pickles the record in binary format with a length prefix, and
+ returns it ready for transmission across the socket.
+ """
+ s = cPickle.dumps(record.__dict__, 1)
+ n = len(s)
+ slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
+ return slen + s
+
+ def handleError(self):
+ """
+ An error has occurred during logging. Most likely cause -
+ connection lost. Close the socket so that we can retry on the
+ next event.
+ """
+ if self.closeOnError and self.sock:
+ self.sock.close()
+ self.sock = None #try to reconnect next time
+
+ def emit(self, record):
+ """
+ Pickles the record and writes it to the socket in binary format.
+ If there is an error with the socket, silently drop the packet.
+ If there was a problem with the socket, re-establishes the
+ socket.
+ """
+ try:
+ s = self.makePickle(record)
+ if not self.sock:
+ self.sock = self.makeSocket()
+ self.send(s)
+ except:
+ self.handleError()
+
+ def close(self):
+ """
+ Closes the socket.
+ """
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+
+class DatagramHandler(SocketHandler):
+ """
+ A handler class which writes logging records, in pickle format, to
+ a datagram socket. Note that the very simple wire protocol used means
+ that packet sizes are expected to be encodable within 16 bits
+ (i.e. < 32767 bytes).
+
+ """
+ def __init__(self, host, port):
+ """
+ Initializes the handler with a specific host address and port.
+ """
+ SocketHandler.__init__(self, host, port)
+ self.closeOnError = 0
+
+ def makeSocket(self):
+ """
+ The factory method of SocketHandler is here overridden to create
+ a UDP socket (SOCK_DGRAM).
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ return s
+
+ def send(self, s):
+ """
+ Send a pickled string to a socket. This function allows for
+ partial sends which can happen when the network is busy.
+ """
+ sentsofar = 0
+ left = len(s)
+ addr = (self.host, self.port)
+ while left > 0:
+ sent = self.sock.sendto(s[sentsofar:], addr)
+ sentsofar = sentsofar + sent
+ left = left - sent
+
+class SysLogHandler(Handler):
+ """
+ A handler class which sends formatted logging records to a syslog
+ server. Based on Sam Rushing's syslog module:
+ http://www.nightmare.com/squirl/python-ext/misc/syslog.py
+ Contributed by Nicolas Untz (after which minor refactoring changes
+ have been made).
+ """
+
+ # from <linux/sys/syslog.h>:
+ # ======================================================================
+ # priorities/facilities are encoded into a single 32-bit quantity, where
+ # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
+ # facility (0-big number). Both the priorities and the facilities map
+ # roughly one-to-one to strings in the syslogd(8) source code. This
+ # mapping is included in this file.
+ #
+ # priorities (these are ordered)
+
+ LOG_EMERG = 0 # system is unusable
+ LOG_ALERT = 1 # action must be taken immediately
+ LOG_CRIT = 2 # critical conditions
+ LOG_ERR = 3 # error conditions
+ LOG_WARNING = 4 # warning conditions
+ LOG_NOTICE = 5 # normal but significant condition
+ LOG_INFO = 6 # informational
+ LOG_DEBUG = 7 # debug-level messages
+
+ # facility codes
+ LOG_KERN = 0 # kernel messages
+ LOG_USER = 1 # random user-level messages
+ LOG_MAIL = 2 # mail system
+ LOG_DAEMON = 3 # system daemons
+ LOG_AUTH = 4 # security/authorization messages
+ LOG_SYSLOG = 5 # messages generated internally by syslogd
+ LOG_LPR = 6 # line printer subsystem
+ LOG_NEWS = 7 # network news subsystem
+ LOG_UUCP = 8 # UUCP subsystem
+ LOG_CRON = 9 # clock daemon
+ LOG_AUTHPRIV = 10 # security/authorization messages (private)
+
+ # other codes through 15 reserved for system use
+ LOG_LOCAL0 = 16 # reserved for local use
+ LOG_LOCAL1 = 17 # reserved for local use
+ LOG_LOCAL2 = 18 # reserved for local use
+ LOG_LOCAL3 = 19 # reserved for local use
+ LOG_LOCAL4 = 20 # reserved for local use
+ LOG_LOCAL5 = 21 # reserved for local use
+ LOG_LOCAL6 = 22 # reserved for local use
+ LOG_LOCAL7 = 23 # reserved for local use
+
+ priority_names = {
+ "alert": LOG_ALERT,
+ "crit": LOG_CRIT,
+ "critical": LOG_CRIT,
+ "debug": LOG_DEBUG,
+ "emerg": LOG_EMERG,
+ "err": LOG_ERR,
+ "error": LOG_ERR, # DEPRECATED
+ "info": LOG_INFO,
+ "notice": LOG_NOTICE,
+ "panic": LOG_EMERG, # DEPRECATED
+ "warn": LOG_WARNING, # DEPRECATED
+ "warning": LOG_WARNING,
+ }
+
+ facility_names = {
+ "auth": LOG_AUTH,
+ "authpriv": LOG_AUTHPRIV,
+ "cron": LOG_CRON,
+ "daemon": LOG_DAEMON,
+ "kern": LOG_KERN,
+ "lpr": LOG_LPR,
+ "mail": LOG_MAIL,
+ "news": LOG_NEWS,
+ "security": LOG_AUTH, # DEPRECATED
+ "syslog": LOG_SYSLOG,
+ "user": LOG_USER,
+ "uucp": LOG_UUCP,
+ "local0": LOG_LOCAL0,
+ "local1": LOG_LOCAL1,
+ "local2": LOG_LOCAL2,
+ "local3": LOG_LOCAL3,
+ "local4": LOG_LOCAL4,
+ "local5": LOG_LOCAL5,
+ "local6": LOG_LOCAL6,
+ "local7": LOG_LOCAL7,
+ }
+
+ def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
+ """
+ If address is specified as a string, UNIX socket is used.
+ If facility is not specified, LOG_USER is used.
+ """
+ Handler.__init__(self)
+
+ self.address = address
+ self.facility = facility
+ if type(address) == types.StringType:
+ self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.socket.connect(address)
+ self.unixsocket = 1
+ else:
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.unixsocket = 0
+
+ self.formatter = None
+
+ # curious: when talking to the unix-domain '/dev/log' socket, a
+ # zero-terminator seems to be required. this string is placed
+ # into a class variable so that it can be overridden if
+ # necessary.
+ log_format_string = '<%d>%s\000'
+
+ def encodePriority (self, facility, priority):
+ """
+ Encode the facility and priority. You can pass in strings or
+ integers - if strings are passed, the facility_names and
+ priority_names mapping dictionaries are used to convert them to
+ integers.
+ """
+ if type(facility) == types.StringType:
+ facility = self.facility_names[facility]
+ if type(priority) == types.StringType:
+ priority = self.priority_names[priority]
+ return (facility << 3) | priority
+
+ def close (self):
+ """
+ Closes the socket.
+ """
+ if self.unixsocket:
+ self.socket.close()
+
+ def emit(self, record):
+ """
+ The record is formatted, and then sent to the syslog server. If
+ exception information is present, it is NOT sent to the server.
+ """
+ msg = self.format(record)
+ """
+ We need to convert record level to lowercase, maybe this will
+ change in the future.
+ """
+ msg = self.log_format_string % (
+ self.encodePriority(self.facility,
+ string.lower(record.levelname)),
+ msg)
+ try:
+ if self.unixsocket:
+ self.socket.send(msg)
+ else:
+ self.socket.sendto(msg, self.address)
+ except:
+ self.handleError()
+
+class SMTPHandler(Handler):
+ """
+ A handler class which sends an SMTP email for each logging event.
+ """
+ def __init__(self, mailhost, fromaddr, toaddrs, subject):
+ """
+ Initialize the instance with the from and to addresses and subject
+ line of the email. To specify a non-standard SMTP port, use the
+ (host, port) tuple format for the mailhost argument.
+ """
+ Handler.__init__(self)
+ if type(mailhost) == types.TupleType:
+ host, port = mailhost
+ self.mailhost = host
+ self.mailport = port
+ else:
+ self.mailhost = mailhost
+ self.mailport = None
+ self.fromaddr = fromaddr
+ self.toaddrs = toaddrs
+ self.subject = subject
+
+ def getSubject(self, record):
+ """
+ If you want to specify a subject line which is record-dependent,
+ override this method.
+ """
+ return self.subject
+
+ def emit(self, record):
+ """
+ Format the record and send it to the specified addressees.
+ """
+ try:
+ import smtplib
+ port = self.mailport
+ if not port:
+ port = smtplib.SMTP_PORT
+ smtp = smtplib.SMTP(self.mailhost, port)
+ msg = self.format(record)
+ msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (
+ self.fromaddr,
+ string.join(self.toaddrs, ","),
+ self.getSubject(record), msg
+ )
+ smtp.sendmail(self.fromaddr, self.toaddrs, msg)
+ smtp.quit()
+ except:
+ self.handleError()
+
+class BufferingHandler(Handler):
+ """
+ A handler class which buffers logging records in memory. Whenever each
+ record is added to the buffer, a check is made to see if the buffer should
+ be flushed. If it should, then flush() is expected to do the needful.
+ """
+ def __init__(self, capacity):
+ """
+ Initialize the handler with the buffer size.
+ """
+ Handler.__init__(self)
+ self.capacity = capacity
+ self.buffer = []
+
+ def shouldFlush(self, record):
+ """
+ Returns true if the buffer is up to capacity. This method can be
+ overridden to implement custom flushing strategies.
+ """
+ return (len(self.buffer) >= self.capacity)
+
+ def emit(self, record):
+ """
+ Append the record. If shouldFlush() tells us to, call flush() to process
+ the buffer.
+ """
+ self.buffer.append(record)
+ if self.shouldFlush(record):
+ self.flush()
+
+ def flush(self):
+ """
+ Override to implement custom flushing behaviour. This version just zaps
+ the buffer to empty.
+ """
+ self.buffer = []
+
+class MemoryHandler(BufferingHandler):
+ """
+ A handler class which buffers logging records in memory, periodically
+ flushing them to a target handler. Flushing occurs whenever the buffer
+ is full, or when an event of a certain severity or greater is seen.
+ """
+ def __init__(self, capacity, flushLevel=ERROR, target=None):
+ """
+ Initialize the handler with the buffer size, the level at which
+ flushing should occur and an optional target. Note that without a
+ target being set either here or via setTarget(), a MemoryHandler
+ is no use to anyone!
+ """
+ BufferingHandler.__init__(self, capacity)
+ self.flushLevel = flushLevel
+ self.target = target
+
+ def shouldFlush(self, record):
+ """
+ Check for buffer full or a record at the flushLevel or higher.
+ """
+ return (len(self.buffer) >= self.capacity) or \
+ (record.levelno >= self.flushLevel)
+
+ def setTarget(self, target):
+ """
+ Set the target handler for this handler.
+ """
+ self.target = target
+
+ def flush(self):
+ """
+ For a MemoryHandler, flushing means just sending the buffered
+ records to the target, if there is one. Override if you want
+ different behaviour.
+ """
+ if self.target:
+ for record in self.buffer:
+ self.target.handle(record)
+ self.buffer = []
+
+ def close(self):
+ """
+ Flush, set the target to None and lose the buffer.
+ """
+ self.flush()
+ self.target = None
+ self.buffer = []
+
+class NTEventLogHandler(Handler):
+ """
+ A handler class which sends events to the NT Event Log. Adds a
+ registry entry for the specified application name. If no dllname is
+ provided, win32service.pyd (which contains some basic message
+ placeholders) is used. Note that use of these placeholders will make
+ your event logs big, as the entire message source is held in the log.
+ If you want slimmer logs, you have to pass in the name of your own DLL
+ which contains the message definitions you want to use in the event log.
+ """
+ def __init__(self, appname, dllname=None, logtype="Application"):
+ Handler.__init__(self)
+ try:
+ import win32evtlogutil, win32evtlog
+ self.appname = appname
+ self._welu = win32evtlogutil
+ if not dllname:
+ dllname = os.path.split(self._welu.__file__)
+ dllname = os.path.split(dllname[0])
+ dllname = os.path.join(dllname[0], r'win32service.pyd')
+ self.dllname = dllname
+ self.logtype = logtype
+ self._welu.AddSourceToRegistry(appname, dllname, logtype)
+ self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
+ self.typemap = {
+ DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
+ INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
+ WARN : win32evtlog.EVENTLOG_WARNING_TYPE,
+ ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
+ CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
+ }
+ except ImportError:
+ print "The Python Win32 extensions for NT (service, event "\
+ "logging) appear not to be available."
+ self._welu = None
+
+ def getMessageID(self, record):
+ """
+ Return the message ID for the event record. If you are using your
+ own messages, you could do this by having the msg passed to the
+ logger being an ID rather than a formatting string. Then, in here,
+ you could use a dictionary lookup to get the message ID. This
+ version returns 1, which is the base message ID in win32service.pyd.
+ """
+ return 1
+
+ def getEventCategory(self, record):
+ """
+ Return the event category for the record. Override this if you
+ want to specify your own categories. This version returns 0.
+ """
+ return 0
+
+ def getEventType(self, record):
+ """
+ Return the event type for the record. Override this if you want
+ to specify your own types. This version does a mapping using the
+ handler's typemap attribute, which is set up in __init__() to a
+ dictionary which contains mappings for DEBUG, INFO, WARN, ERROR
+ and CRITICAL. If you are using your own levels you will either need
+ to override this method or place a suitable dictionary in the
+ handler's typemap attribute.
+ """
+ return self.typemap.get(record.levelno, self.deftype)
+
+ def emit(self, record):
+ """
+ Determine the message ID, event category and event type. Then
+ log the message in the NT event log.
+ """
+ if self._welu:
+ try:
+ id = self.getMessageID(record)
+ cat = self.getEventCategory(record)
+ type = self.getEventType(record)
+ msg = self.format(record)
+ self._welu.ReportEvent(self.appname, id, cat, type, [msg])
+ except:
+ self.handleError()
+
+ def close(self):
+ """
+ You can remove the application name from the registry as a
+ source of event log entries. However, if you do this, you will
+ not be able to see the events as you intended in the Event Log
+ Viewer - it needs to be able to access the registry to get the
+ DLL name.
+ """
+ #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
+ pass
+
+class HTTPHandler(Handler):
+ """
+ A class which sends records to a Web server, using either GET or
+ POST semantics.
+ """
+ def __init__(self, host, url, method="GET"):
+ """
+ Initialize the instance with the host, the request URL, and the method
+ ("GET" or "POST")
+ """
+ Handler.__init__(self)
+ method = string.upper(method)
+ if method not in ["GET", "POST"]:
+ raise ValueError, "method must be GET or POST"
+ self.host = host
+ self.url = url
+ self.method = method
+
+ def emit(self, record):
+ """
+ Send the record to the Web server as an URL-encoded dictionary
+ """
+ try:
+ import httplib, urllib
+ h = httplib.HTTP(self.host)
+ url = self.url
+ data = urllib.urlencode(record.__dict__)
+ if self.method == "GET":
+ if (string.find(url, '?') >= 0):
+ sep = '&'
+ else:
+ sep = '?'
+ url = url + "%c%s" % (sep, data)
+ h.putrequest(self.method, url)
+ if self.method == "POST":
+ h.putheader("Content-length", str(len(data)))
+ h.endheaders()
+ if self.method == "POST":
+ h.send(data)
+ h.getreply() #can't do anything with the result
+ except:
+ self.handleError()
+
+#---------------------------------------------------------------------------
+# Manager classes and functions
+#---------------------------------------------------------------------------
+
+class PlaceHolder:
+ """
+ PlaceHolder instances are used in the Manager logger hierarchy to take
+ the place of nodes for which no loggers have been defined [FIXME add
+ example].
+ """
+ def __init__(self, alogger):
+ """
+ Initialize with the specified logger being a child of this placeholder.
+ """
+ self.loggers = [alogger]
+
+ def append(self, alogger):
+ """
+ Add the specified logger as a child of this placeholder.
+ """
+ if alogger not in self.loggers:
+ self.loggers.append(alogger)
+
+#
+# Determine which class to use when instantiating loggers.
+#
+_loggerClass = None
+
+def setLoggerClass(klass):
+ """
+ Set the class to be used when instantiating a logger. The class should
+ define __init__() such that only a name argument is required, and the
+ __init__() should call Logger.__init__()
+ """
+ if klass != Logger:
+ if type(klass) != types.ClassType:
+ raise TypeError, "setLoggerClass is expecting a class"
+ if not issubclass(klass, Logger):
+ raise TypeError, "logger not derived from logging.Logger: " + \
+ klass.__name__
+ global _loggerClass
+ _loggerClass = klass
+
+class Manager:
+ """
+ There is [under normal circumstances] just one Manager instance, which
+ holds the hierarchy of loggers.
+ """
+ def __init__(self, root):
+ """
+ Initialize the manager with the root node of the logger hierarchy.
+ """
+ self.root = root
+ self.disable = 0
+ self.emittedNoHandlerWarning = 0
+ self.loggerDict = {}
+
+ def getLogger(self, name):
+ """
+ Get a logger with the specified name, creating it if it doesn't
+ yet exist. If a PlaceHolder existed for the specified name [i.e.
+ the logger didn't exist but a child of it did], replace it with
+ the created logger and fix up the parent/child references which
+ pointed to the placeholder to now point to the logger.
+ """
+ rv = None
+ _acquireLock()
+ try:
+ if self.loggerDict.has_key(name):
+ rv = self.loggerDict[name]
+ if isinstance(rv, PlaceHolder):
+ ph = rv
+ rv = _loggerClass(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupChildren(ph, rv)
+ self._fixupParents(rv)
+ else:
+ rv = _loggerClass(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupParents(rv)
+ finally:
+ _releaseLock()
+ return rv
+
+ def _fixupParents(self, alogger):
+ """
+ Ensure that there are either loggers or placeholders all the way
+ from the specified logger to the root of the logger hierarchy.
+ """
+ name = alogger.name
+ i = string.rfind(name, ".")
+ rv = None
+ while (i > 0) and not rv:
+ substr = name[:i]
+ if not self.loggerDict.has_key(substr):
+ self.loggerDict[substr] = PlaceHolder(alogger)
+ else:
+ obj = self.loggerDict[substr]
+ if isinstance(obj, Logger):
+ rv = obj
+ else:
+ assert isinstance(obj, PlaceHolder)
+ obj.append(alogger)
+ i = string.rfind(name, ".", 0, i - 1)
+ if not rv:
+ rv = self.root
+ alogger.parent = rv
+
+ def _fixupChildren(self, ph, alogger):
+ """
+ Ensure that children of the placeholder ph are connected to the
+ specified logger.
+ """
+ for c in ph.loggers:
+ if string.find(c.parent.name, alogger.name) <> 0:
+ alogger.parent = c.parent
+ c.parent = alogger
+
+#---------------------------------------------------------------------------
+# Logger classes and functions
+#---------------------------------------------------------------------------
+
+class Logger(Filterer):
+ """
+ Instances of the Logger class represent a single logging channel. A
+ "logging channel" indicates an area of an application. Exactly how an
+ "area" is defined is up to the application developer. Since an
+ application can have any number of areas, logging channels are identified
+ by a unique string. Application areas can be nested (e.g. an area
+ of "input processing" might include sub-areas "read CSV files", "read
+ XLS files" and "read Gnumeric files"). To cater for this natural nesting,
+ channel names are organized into a namespace hierarchy where levels are
+ separated by periods, much like the Java or Python package namespace. So
+ in the instance given above, channel names might be "input" for the upper
+ level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
+ There is no arbitrary limit to the depth of nesting.
+ """
+ def __init__(self, name, level=ALL):
+ """
+ Initialize the logger with a name and an optional level.
+ """
+ Filterer.__init__(self)
+ self.name = name
+ self.level = level
+ self.parent = None
+ self.propagate = 1
+ self.handlers = []
+ self.disabled = 0
+
+ def setLevel(self, lvl):
+ """
+ Set the logging level of this logger.
+ """
+ self.level = lvl
+
+# def getRoot(self):
+# """
+# Get the root of the logger hierarchy.
+# """
+# return Logger.root
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'DEBUG'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
+ """
+ if self.manager.disable >= DEBUG:
+ return
+ if DEBUG >= self.getEffectiveLevel():
+ apply(self._log, (DEBUG, msg, args), kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'INFO'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
+ """
+ if self.manager.disable >= INFO:
+ return
+ if INFO >= self.getEffectiveLevel():
+ apply(self._log, (INFO, msg, args), kwargs)
+
+ def warn(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'WARN'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.warn("Houston, we have a %s", "bit of a problem", exc_info=1)
+ """
+ if self.manager.disable >= WARN:
+ return
+ if self.isEnabledFor(WARN):
+ apply(self._log, (WARN, msg, args), kwargs)
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'ERROR'. To pass exception information,
+ use the keyword argument exc_info with a true value, e.g.
+
+ logger.error("Houston, we have a %s", "major problem", exc_info=1)
+ """
+ if self.manager.disable >= ERROR:
+ return
+ if self.isEnabledFor(ERROR):
+ apply(self._log, (ERROR, msg, args), kwargs)
+
+ def exception(self, msg, *args):
+ """
+ Convenience method for logging an ERROR with exception information
+ """
+ apply(self.error, (msg,) + args, {'exc_info': 1})
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'CRITICAL'. To pass exception
+ information, use the keyword argument exc_info with a true value, e.g.
+
+ logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
+ """
+ if self.manager.disable >= CRITICAL:
+ return
+ if CRITICAL >= self.getEffectiveLevel():
+ apply(self._log, (CRITICAL, msg, args), kwargs)
+
+ fatal = critical
+
+ def log(self, lvl, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the severity 'lvl'. To pass exception
+ information, use the keyword argument exc_info with a true value, e.g.
+ logger.log(lvl, "We have a %s", "mysterious problem", exc_info=1)
+ """
+ if self.manager.disable >= lvl:
+ return
+ if self.isEnabledFor(lvl):
+ apply(self._log, (lvl, msg, args), kwargs)
+
+ def findCaller(self):
+ """
+ Find the stack frame of the caller so that we can note the source
+ file name and line number.
+ """
+ rv = (None, None)
+ frame = inspect.currentframe().f_back
+ while frame:
+ sfn = inspect.getsourcefile(frame)
+ if sfn:
+ sfn = os.path.normcase(sfn)
+ if sfn != _srcfile:
+ #print frame.f_code.co_code
+ lineno = inspect.getlineno(frame)
+ rv = (sfn, lineno)
+ break
+ frame = frame.f_back
+ return rv
+
+ def makeRecord(self, name, lvl, fn, lno, msg, args, exc_info):
+ """
+ A factory method which can be overridden in subclasses to create
+ specialized LogRecords.
+ """
+ return LogRecord(name, lvl, fn, lno, msg, args, exc_info)
+
+ def _log(self, lvl, msg, args, exc_info=None):
+ """
+ Low-level logging routine which creates a LogRecord and then calls
+ all the handlers of this logger to handle the record.
+ """
+ if inspect and _srcfile:
+ _acquireLock()
+ try:
+ fn, lno = self.findCaller()
+ finally:
+ _releaseLock()
+ else:
+ fn, lno = "<unknown file>", 0
+ if exc_info:
+ exc_info = sys.exc_info()
+ record = self.makeRecord(self.name, lvl, fn, lno, msg, args, exc_info)
+ self.handle(record)
+
+ def handle(self, record):
+ """
+ Call the handlers for the specified record. This method is used for
+ unpickled records received from a socket, as well as those created
+ locally. Logger-level filtering is applied.
+ """
+ if (not self.disabled) and self.filter(record):
+ self.callHandlers(record)
+
+ def addHandler(self, hdlr):
+ """
+ Add the specified handler to this logger.
+ """
+ if not (hdlr in self.handlers):
+ self.handlers.append(hdlr)
+
+ def removeHandler(self, hdlr):
+ """
+ Remove the specified handler from this logger.
+ """
+ if hdlr in self.handlers:
+ hdlr.close()
+ self.handlers.remove(hdlr)
+
+ def callHandlers(self, record):
+ """
+ Loop through all handlers for this logger and its parents in the
+ logger hierarchy. If no handler was found, output a one-off error
+ message to sys.stderr. Stop searching up the hierarchy whenever a
+ logger with the "propagate" attribute set to zero is found - that
+ will be the last logger whose handlers are called.
+ """
+ c = self
+ found = 0
+ while c:
+ for hdlr in c.handlers:
+ found = found + 1
+ if record.levelno >= hdlr.level:
+ hdlr.handle(record)
+ if not c.propagate:
+ c = None #break out
+ else:
+ c = c.parent
+ if (found == 0) and not self.manager.emittedNoHandlerWarning:
+ sys.stderr.write("No handlers could be found for logger"
+ " \"%s\"\n" % self.name)
+ self.manager.emittedNoHandlerWarning = 1
+
+ def getEffectiveLevel(self):
+ """
+ Loop through this logger and its parents in the logger hierarchy,
+ looking for a non-zero logging level. Return the first one found.
+ """
+ logger = self
+ while logger:
+ if logger.level:
+ return logger.level
+ logger = logger.parent
+ return ALL
+
+ def isEnabledFor(self, lvl):
+ """
+ Is this logger enabled for level lvl?
+ """
+ if self.manager.disable >= lvl:
+ return 0
+ return lvl >= self.getEffectiveLevel()
+
+class RootLogger(Logger):
+ """
+ A root logger is not that different to any other logger, except that
+ it must have a logging level and there is only one instance of it in
+ the hierarchy.
+ """
+ def __init__(self, lvl):
+ """
+ Initialize the logger with the name "root".
+ """
+ Logger.__init__(self, "root", lvl)
+
+_loggerClass = Logger
+
+root = RootLogger(DEBUG)
+Logger.root = root
+Logger.manager = Manager(Logger.root)
+
+#---------------------------------------------------------------------------
+# Configuration classes and functions
+#---------------------------------------------------------------------------
+
+BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
+
+def basicConfig():
+ """
+ Do basic configuration for the logging system by creating a
+ StreamHandler with a default Formatter and adding it to the
+ root logger.
+ """
+ if len(root.handlers) == 0:
+ hdlr = StreamHandler()
+ fmt = Formatter(BASIC_FORMAT)
+ hdlr.setFormatter(fmt)
+ root.addHandler(hdlr)
+
+def fileConfig(fname):
+ """
+ Read the logging configuration from a ConfigParser-format file. This can
+ be called several times from an application, allowing an end user the
+ ability to select from various pre-canned configurations (if the
+ developer provides a mechanism to present the choices and load the chosen
+ configuration).
+ """
+ import ConfigParser
+
+ cp = ConfigParser.ConfigParser()
+ cp.read(fname)
+ #first, do the formatters...
+ flist = cp.get("formatters", "keys")
+ if len(flist):
+ flist = string.split(flist, ",")
+ formatters = {}
+ for form in flist:
+ sectname = "formatter_%s" % form
+ opts = cp.options(sectname)
+ if "format" in opts:
+ fs = cp.get(sectname, "format", 1)
+ else:
+ fs = None
+ if "datefmt" in opts:
+ dfs = cp.get(sectname, "datefmt", 1)
+ else:
+ dfs = None
+ f = Formatter(fs, dfs)
+ formatters[form] = f
+ #next, do the handlers...
+ #critical section...
+ _acquireLock()
+ try:
+ try:
+ #first, lose the existing handlers...
+ _handlers.clear()
+ #now set up the new ones...
+ hlist = cp.get("handlers", "keys")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ handlers = {}
+ fixups = [] #for inter-handler references
+ for hand in hlist:
+ sectname = "handler_%s" % hand
+ klass = cp.get(sectname, "class")
+ opts = cp.options(sectname)
+ if "formatter" in opts:
+ fmt = cp.get(sectname, "formatter")
+ else:
+ fmt = ""
+ klass = eval(klass)
+ args = cp.get(sectname, "args")
+ args = eval(args)
+ h = apply(klass, args)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ h.setLevel(_levelNames[lvl])
+ if len(fmt):
+ h.setFormatter(formatters[fmt])
+ #temporary hack for FileHandler and MemoryHandler.
+ if klass == FileHandler:
+ maxsize = 0
+ if "maxsize" in opts:
+ ms = cp.getint(sectname, "maxsize")
+ if ms > 0:
+ maxsize = ms
+ if maxsize:
+ backcount = 0
+ if "backcount" in opts:
+ bc = cp.getint(sectname, "backcount")
+ if bc > 0:
+ backcount = bc
+ h.setRollover(maxsize, backcount)
+ elif klass == MemoryHandler:
+ if "target" in opts:
+ target = cp.get(sectname,"target")
+ else:
+ target = ""
+ if len(target): #the target handler may not be loaded yet, so keep for later...
+ fixups.append((h, target))
+ handlers[hand] = h
+ #now all handlers are loaded, fixup inter-handler references...
+ for fixup in fixups:
+ h = fixup[0]
+ t = fixup[1]
+ h.setTarget(handlers[t])
+ #at last, the loggers...first the root...
+ llist = cp.get("loggers", "keys")
+ llist = string.split(llist, ",")
+ llist.remove("root")
+ sectname = "logger_root"
+ log = root
+ opts = cp.options(sectname)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ log.setLevel(_levelNames[lvl])
+ for h in root.handlers:
+ root.removeHandler(h)
+ hlist = cp.get(sectname, "handlers")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ for hand in hlist:
+ log.addHandler(handlers[hand])
+ #and now the others...
+ #we don't want to lose the existing loggers,
+ #since other threads may have pointers to them.
+ #existing is set to contain all existing loggers,
+ #and as we go through the new configuration we
+ #remove any which are configured. At the end,
+ #what's left in existing is the set of loggers
+ #which were in the previous configuration but
+ #which are not in the new configuration.
+ existing = root.manager.loggerDict.keys()
+ #now set up the new ones...
+ for log in llist:
+ sectname = "logger_%s" % log
+ qn = cp.get(sectname, "qualname")
+ opts = cp.options(sectname)
+ if "propagate" in opts:
+ propagate = cp.getint(sectname, "propagate")
+ else:
+ propagate = 1
+ logger = getLogger(qn)
+ if qn in existing:
+ existing.remove(qn)
+ if "level" in opts:
+ lvl = cp.get(sectname, "level")
+ logger.setLevel(_levelNames[lvl])
+ for h in logger.handlers:
+ logger.removeHandler(h)
+ logger.propagate = propagate
+ logger.disabled = 0
+ hlist = cp.get(sectname, "handlers")
+ if len(hlist):
+ hlist = string.split(hlist, ",")
+ for hand in hlist:
+ logger.addHandler(handlers[hand])
+ #Disable any old loggers. There's no point deleting
+ #them as other threads may continue to hold references
+ #and by disabling them, you stop them doing any logging.
+ for log in existing:
+ root.manager.loggerDict[log].disabled = 1
+ except:
+ import traceback
+ ei = sys.exc_info()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
+ del ei
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Utility functions at module level.
+# Basically delegate everything to the root logger.
+#---------------------------------------------------------------------------
+
+def getLogger(name=None):
+ """
+ Return a logger with the specified name, creating it if necessary.
+ If no name is specified, return the root logger.
+ """
+ if name:
+ return Logger.manager.getLogger(name)
+ else:
+ return root
+
+def getRootLogger():
+ """
+ Return the root logger. Note that getLogger('') now does the same thing,
+ so this function is deprecated and may disappear in the future.
+ """
+ return root
+
+def critical(msg, *args, **kwargs):
+ """
+ Log a message with severity 'CRITICAL' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.critical, (msg,)+args, kwargs)
+
+fatal = critical
+
+def error(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.error, (msg,)+args, kwargs)
+
+def exception(msg, *args):
+ """
+ Log a message with severity 'ERROR' on the root logger,
+ with exception information.
+ """
+ apply(error, (msg,)+args, {'exc_info': 1})
+
+def warn(msg, *args, **kwargs):
+ """
+ Log a message with severity 'WARN' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.warn, (msg,)+args, kwargs)
+
+def info(msg, *args, **kwargs):
+ """
+ Log a message with severity 'INFO' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.info, (msg,)+args, kwargs)
+
+def debug(msg, *args, **kwargs):
+ """
+ Log a message with severity 'DEBUG' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ apply(root.debug, (msg,)+args, kwargs)
+
+def disable(level):
+ """
+ Disable all logging calls less severe than 'level'.
+ """
+ root.manager.disable = level
+
+def shutdown():
+ """
+ Perform any cleanup actions in the logging system (e.g. flushing
+ buffers). Should be called at application exit.
+ """
+ for h in _handlers.keys():
+ h.flush()
+ h.close()
+
+#
+# The following code implements a socket listener for on-the-fly
+# reconfiguration of logging.
+#
+# _listener holds the server object doing the listening
+_listener = None
+
+def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
+ """
+ Start up a socket server on the specified port, and listen for new
+ configurations. These will be sent as a file suitable for processing
+ by fileConfig(). Returns a Thread object on which you can call start()
+ to start the server, and which you can join() when appropriate.
+ To stop the server, call stopListening().
+ """
+ if not thread:
+ raise NotImplementedError, "listen() needs threading to work"
+
+ import threading
+
+ class ConfigStreamHandler(StreamRequestHandler):
+ """
+ Handler for a logging configuration request. It expects a
+ completely new logging configuration and uses fileConfig to
+ install it.
+ """
+ def handle(self):
+ """
+ Each request is expected to be a 2-byte length,
+ followed by the config file. Uses fileConfig() to do the
+ needful.
+ """
+ import tempfile
+ try:
+ conn = self.connection
+ chunk = conn.recv(2)
+ if len(chunk) == 2:
+ slen = (ord(chunk[0]) << 8) | ord(chunk[1])
+ chunk = self.connection.recv(slen)
+ while len(chunk) < slen:
+ chunk = chunk + conn.recv(slen - len(chunk))
+ #Apply new configuration. We'd like to be able to
+ #create a StringIO and pass that in, but unfortunately
+ #1.5.2 ConfigParser does not support reading file
+ #objects, only actual files. So we create a temporary
+ #file and remove it later.
+ file = tempfile.mktemp(".ini")
+ f = open(file, "w")
+ f.write(chunk)
+ f.close()
+ fileConfig(file)
+ os.remove(file)
+ except socket.error, e:
+ if type(e.args) != types.TupleType:
+ raise
+ else:
+ errcode = e.args[0]
+ if errcode != RESET_ERROR:
+ raise
+
+ class ConfigSocketReceiver(ThreadingTCPServer):
+ """
+ A simple TCP socket-based logging config receiver.
+ """
+
+ allow_reuse_address = 1
+
+ def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
+ handler=None):
+ ThreadingTCPServer.__init__(self, (host, port), handler)
+ _acquireLock()
+ self.abort = 0
+ _releaseLock()
+ self.timeout = 1
+
+ def serve_until_stopped(self):
+ import select
+ abort = 0
+ while not abort:
+ rd, wr, ex = select.select([self.socket.fileno()],
+ [], [],
+ self.timeout)
+ if rd:
+ self.handle_request()
+ _acquireLock()
+ abort = self.abort
+ _releaseLock()
+
+ def serve(rcvr, hdlr):
+ server = rcvr(handler=hdlr)
+ global _listener
+ _acquireLock()
+ _listener = server
+ _releaseLock()
+ server.serve_until_stopped()
+
+ return threading.Thread(target=serve, args=(ConfigSocketReceiver, ConfigStreamHandler))
+
+def stopListening():
+ """
+ Stop the listening server which was created with a call to listen().
+ """
+ if _listener:
+ _acquireLock()
+ _listener.abort = 1
+ _listener = None
+ _releaseLock()
+
+
+# bicycle repair man stuff
+
+bike_logger_initialised = 0
+
+def init():
+ global bike_logger_initialised
+ if not bike_logger_initialised:
+ log = getLogger("bike")
+ h = StreamHandler()
+ h.setFormatter(Formatter(
+ fmt="%(pathname)s:%(lineno)s:%(levelname)s %(message)s"))
+ log.addHandler(h)
+ bike_logger_initialised = 1
+
+if __name__ == "__main__":
+ print __doc__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/mock.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,143 @@
+#
+# (c) Dave Kirby 2001
+# dkirby@bigfoot.com
+#
+# Call interceptor code by Phil Dawes (pdawes@users.sourceforge.net)
+
+'''
+The Mock class emulates any other class for testing purposes.
+All method calls are stored for later examination.
+The class constructor takes a dictionary of method names and the values
+they return. Methods that are not in the dictionary will return None.
+'''
+import inspect
+
+
+class Mock:
+ def __init__(self, returnValues=None ):
+ self.mockCalledMethods = {}
+ self.mockAllCalledMethods = []
+ self.mockReturnValues = returnValues or {}
+ self.setupMethodInterceptors()
+
+ def setupMethodInterceptors(self):
+ if self.__class__ != Mock: # check we've been subclassed
+ methods = inspect.getmembers(self.__class__,inspect.ismethod)
+ for m in methods:
+ name = m[0]
+ self.__dict__[name] = MethodCallInterceptor(name,self)
+
+ def __getattr__( self, name ):
+ return MockCaller( name, self )
+
+ def getAllCalls(self):
+ '''return a list of MockCall objects,
+ representing all the methods in the order they were called'''
+ return self.mockAllCalledMethods
+
+ def getNamedCalls(self, methodName ):
+ '''return a list of MockCall objects,
+ representing all the calls to the named method in the order they were called'''
+ return self.mockCalledMethods.get(methodName, [] )
+
+ def assertNamedCall(self,methodName,*args):
+ # assert call was made once
+ assert(len(self.getNamedCalls(methodName)) == 1)
+ # assert args are correct
+ argsdict = inspect.getargvalues(inspect.currentframe())[3]
+ i=0
+ for arg in argsdict['args']:
+ assert(self.getNamedCalls(methodName)[0].getParam(i) == arg)
+ i += 1
+
+ def assertCallInOrder(self,methodName,*args):
+ ''' Convenience method to allow client to check that calls were
+ made in the right order. Call once for each methodcall'''
+
+ # initialise callIndex. (n.b. __getattr__ method complicates
+ # this since attrs are MockCaller instances by default)
+ if type(self.callIndex) != type(1):
+ self.callIndex=0
+
+ call = self.getAllCalls()[self.callIndex]
+ # assert method name is correct
+ assert(call.getName() == methodName)
+ # assert args are correct
+ argsdict = inspect.getargvalues(inspect.currentframe())[3]
+ i=0
+ for arg in argsdict['args']:
+ assert(call.getParam(i) == arg)
+ i += 1
+ # assert num args are correct
+ assert(call.getNumParams() == i)
+ self.callIndex += 1
+
+ def assertNoMoreCalls(self):
+ assert(len(self.getAllCalls()) == self.callIndex)
+
+class MockCall:
+ def __init__(self, name, params, kwparams ):
+ self.name = name
+ self.params = params
+ self.kwparams = kwparams
+ def getParam( self, n ):
+ if type(n) == type(1):
+ return self.params[n]
+ elif type(n) == type(''):
+ return self.kwparams[n]
+ else:
+ raise IndexError, 'illegal index type for getParam'
+
+ def getNumParams(self):
+ return len(self.params)
+
+
+ def getName(self):
+ return self.name
+
+ #pretty-print the method call
+ def __str__(self):
+ s = self.name + "("
+ sep = ''
+ for p in self.params:
+ s = s + sep + repr(p)
+ sep = ', '
+ for k,v in self.kwparams.items():
+ s = s + sep + k+ '='+repr(v)
+ sep = ', '
+ s = s + ')'
+ return s
+ def __repr__(self):
+ return self.__str__()
+
+class MockCaller:
+ def __init__( self, name, mock):
+ self.name = name
+ self.mock = mock
+ def __call__(self, *params, **kwparams ):
+ self.recordCall(params,kwparams)
+ return self.mock.mockReturnValues.get(self.name)
+
+ def recordCall(self,params,kwparams):
+ thisCall = MockCall( self.name, params, kwparams )
+ calls = self.mock.mockCalledMethods.get(self.name, [] )
+ if calls == []:
+ self.mock.mockCalledMethods[self.name] = calls
+ calls.append(thisCall)
+ self.mock.mockAllCalledMethods.append(thisCall)
+
+
+# intercepts the call and records it, then delegates to the real call
+class MethodCallInterceptor(MockCaller):
+
+ def __call__(self, *params, **kwparams ):
+ self.recordCall(params,kwparams)
+ return self.makeCall(params)
+
+ def makeCall(self,params):
+ argsstr="(self.mock"
+ for i in range(len(params)):
+ argsstr += ",params["+`i`+"]"
+ argsstr+=")"
+ return eval("self.mock.__class__."+self.name+argsstr)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,3 @@
+#from addtypeinfo import addtypeinfo
+#from load import load
+#from brmtransformer import parse
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/constants.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,3 @@
+messages={"PARSING":"Parsing","ADDTYPEINFO":"Deducing Type Information"}
+
+MAXCALLDEPTH = 10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/fastparser.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+from bike.parsing.fastparserast import *
+from bike.parsing.parserutils import *
+from parser import ParserError
+#import exceptions
+
+indentRE = re.compile("^\s*(\w+)")
+
+# returns a tree of objects representing nested classes and functions
+# in the source
+def fastparser(src,modulename="",filename=""):
+ try:
+ return fastparser_impl(src,modulename,filename)
+ except RuntimeError, ex: # if recursive call exceeds maximum depth
+ if str(ex) == "maximum recursion limit exceeded":
+ raise ParserError,"maximum recursion depth exceeded when fast-parsing src "+filename
+ else:
+ raise
+
+def fastparser_impl(src,modulename,filename):
+ lines = src.splitlines(1)
+ maskedSrc = maskPythonKeywordsInStringsAndComments(src)
+ maskedLines = maskedSrc.splitlines(1)
+ root = Module(filename,modulename,lines,maskedSrc)
+ parentnode = root
+ lineno = 0
+ for line in maskedLines:
+ lineno+=1
+ #print "line",lineno,":",line
+ m = indentRE.match(line)
+ if m:
+ indent = m.start(1)
+ tokenstr = m.group(1)
+ if tokenstr == "import" or tokenstr == "from":
+ while indent <= parentnode.indent: # root indent is -TABWIDTH
+ parentnode = parentnode.getParent()
+ try:
+ parentnode.importlines.append(lineno)
+ except AttributeError:
+ parentnode.importlines = [lineno]
+ elif tokenstr == "class":
+ m2 = classNameRE.match(line)
+ if m2:
+ n = Class(m2.group(1), filename, root, lineno, indent, lines, maskedSrc)
+ root.flattenedNodes.append(n)
+
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+ parentnode.addChild(n)
+ parentnode = n
+
+ elif tokenstr == "def":
+ m2 = fnNameRE.match(line)
+ if m2:
+ n = Function(m2.group(1), filename, root, lineno, indent, lines, maskedSrc)
+ root.flattenedNodes.append(n)
+
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+ parentnode.addChild(n)
+ parentnode = n
+
+ elif indent <= parentnode.indent and \
+ tokenstr in ['if','for','while','try']:
+ parentnode = parentnode.getParent()
+ while indent <= parentnode.indent:
+ parentnode = parentnode.getParent()
+
+ return root
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/fastparserast.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,327 @@
+from __future__ import generators
+from parserutils import generateLogicalLines, maskStringsAndComments, maskStringsAndRemoveComments
+import re
+import os
+import compiler
+from bike.transformer.save import resetOutputQueue
+
+TABWIDTH = 4
+
+classNameRE = re.compile("^\s*class\s+(\w+)")
+fnNameRE = re.compile("^\s*def\s+(\w+)")
+
+_root = None
+
+def getRoot():
+ global _root
+ if _root is None:
+ resetRoot()
+ return _root
+
+def resetRoot(root = None):
+ global _root
+ _root = root or Root()
+ _root.unittestmode = False
+ resetOutputQueue()
+
+
+def getModule(filename_path):
+ from bike.parsing.load import CantLocateSourceNodeException, getSourceNode
+ try:
+ sourcenode = getSourceNode(filename_path)
+ return sourcenode.fastparseroot
+ except CantLocateSourceNodeException:
+ return None
+
+def getPackage(directory_path):
+ from bike.parsing.pathutils import getRootDirectory
+ rootdir = getRootDirectory(directory_path)
+ if rootdir == directory_path:
+ return getRoot()
+ else:
+ return Package(directory_path,
+ os.path.basename(directory_path))
+
+
+
+
+
+class Root:
+ def __init__(self, pythonpath = None):
+ # singleton hack to allow functions in query package to appear
+ # 'stateless'
+ resetRoot(self)
+
+ # this is to get round a python optimisation which reuses an
+ # empty list as a default arg. unfortunately the client of
+ # this method may fill that list, so it's not empty
+ if not pythonpath:
+ pythonpath = []
+ self.pythonpath = pythonpath
+
+ def __repr__(self):
+ return "Root()"
+ #return "Root(%s)"%(self.getChildNodes())
+
+
+ # dummy method
+ def getChild(self,name):
+ return None
+
+class Package:
+ def __init__(self, path, name):
+ self.path = path
+ self.name = name
+
+ def getChild(self,name):
+ from bike.parsing.newstuff import getModule
+ return getModule(os.path.join(self.path,name+".py"))
+
+ def __repr__(self):
+ return "Package(%s,%s)"%(self.path, self.name)
+
+# used so that linenum can be an attribute
+class Line(str):
+ pass
+
+class StructuralNode:
+ def __init__(self, filename, srclines, modulesrc):
+ self.childNodes = []
+ self.filename = filename
+ self._parent = None
+ self._modulesrc = modulesrc
+ self._srclines = srclines
+ self._maskedLines = None
+
+ def addChild(self, node):
+ self.childNodes.append(node)
+ node.setParent(self)
+
+ def setParent(self, parent):
+ self._parent = parent
+
+ def getParent(self):
+ return self._parent
+
+ def getChildNodes(self):
+ return self.childNodes
+
+ def getChild(self,name):
+ matches = [c for c in self.getChildNodes() if c.name == name]
+ if matches != []:
+ return matches[0]
+
+ def getLogicalLine(self,physicalLineno):
+ return generateLogicalLines(self._srclines[physicalLineno-1:]).next()
+
+ # badly named: actually returns line numbers of import statements
+ def getImportLineNumbers(self):
+ try:
+ return self.importlines
+ except AttributeError:
+ return[]
+
+ def getLinesNotIncludingThoseBelongingToChildScopes(self):
+ srclines = self.getMaskedModuleLines()
+ lines = []
+ lineno = self.getStartLine()
+ for child in self.getChildNodes():
+ lines+=srclines[lineno-1: child.getStartLine()-1]
+ lineno = child.getEndLine()
+ lines+=srclines[lineno-1: self.getEndLine()-1]
+ return lines
+
+
+ def generateLinesNotIncludingThoseBelongingToChildScopes(self):
+ srclines = self.getMaskedModuleLines()
+ lines = []
+ lineno = self.getStartLine()
+ for child in self.getChildNodes():
+ for line in srclines[lineno-1: child.getStartLine()-1]:
+ yield self.attachLinenum(line,lineno)
+ lineno +=1
+ lineno = child.getEndLine()
+ for line in srclines[lineno-1: self.getEndLine()-1]:
+ yield self.attachLinenum(line,lineno)
+ lineno +=1
+
+ def generateLinesWithLineNumbers(self,startline=1):
+ srclines = self.getMaskedModuleLines()
+ for lineno in range(startline,len(srclines)+1):
+ yield self.attachLinenum(srclines[lineno-1],lineno)
+
+ def attachLinenum(self,line,lineno):
+ line = Line(line)
+ line.linenum = lineno
+ return line
+
+ def getMaskedModuleLines(self):
+ from bike.parsing.load import Cache
+ try:
+ maskedlines = Cache.instance.maskedlinescache[self.filename]
+ except:
+ # make sure src is actually masked
+ # (could just have keywords masked)
+ maskedsrc = maskStringsAndComments(self._modulesrc)
+ maskedlines = maskedsrc.splitlines(1)
+ Cache.instance.maskedlinescache[self.filename] = maskedlines
+ return maskedlines
+
+
+class Module(StructuralNode):
+ def __init__(self, filename, name, srclines, maskedsrc):
+ StructuralNode.__init__(self, filename, srclines, maskedsrc)
+ self.name = name
+ self.indent = -TABWIDTH
+ self.flattenedNodes = []
+ self.module = self
+
+ def getMaskedLines(self):
+ return self.getMaskedModuleLines()
+
+ def getFlattenedListOfChildNodes(self):
+ return self.flattenedNodes
+
+ def getStartLine(self):
+ return 1
+
+ def getEndLine(self):
+ return len(self.getMaskedModuleLines())+1
+
+ def getSourceNode(self):
+ return self.sourcenode
+
+ def setSourceNode(self, sourcenode):
+ self.sourcenode = sourcenode
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Module) and \
+ node.name == self.name
+
+ def getParent(self):
+ if self._parent is not None:
+ return self._parent
+ else:
+ from newstuff import getPackage
+ return getPackage(os.path.dirname(self.filename))
+
+
+ def __str__(self):
+ return "bike:Module:"+self.filename
+
+indentRE = re.compile("^(\s*)\S")
+class Node:
+ # module = the module node
+ # linenum = starting line number
+ def __init__(self, name, module, linenum, indent):
+ self.name = name
+ self.module = module
+ self.linenum = linenum
+ self.endline = None
+ self.indent = indent
+
+ def getMaskedLines(self):
+ return self.getMaskedModuleLines()[self.getStartLine()-1:self.getEndLine()-1]
+
+ def getStartLine(self):
+ return self.linenum
+
+ def getEndLine(self):
+ if self.endline is None:
+ physicallines = self.getMaskedModuleLines()
+ lineno = self.linenum
+ logicallines = generateLogicalLines(physicallines[lineno-1:])
+
+ # skip the first line, because it's the declaration
+ line = logicallines.next()
+ lineno+=line.count("\n")
+
+ # scan to the end of the fn
+ for line in logicallines:
+ #print lineno,":",line,
+ match = indentRE.match(line)
+ if match and match.end()-1 <= self.indent:
+ break
+ lineno+=line.count("\n")
+ self.endline = lineno
+ return self.endline
+
+ # linenum starts at 0
+ def getLine(self, linenum):
+ return self._srclines[(self.getStartLine()-1) + linenum]
+
+
+baseClassesRE = re.compile("class\s+[^(]+\(([^)]+)\):")
+
+class Class(StructuralNode, Node):
+ def __init__(self, name, filename, module, linenum, indent, srclines, maskedmodulesrc):
+ StructuralNode.__init__(self, filename, srclines, maskedmodulesrc)
+ Node.__init__(self, name, module, linenum, indent)
+ self.type = "Class"
+
+
+ def getBaseClassNames(self):
+ #line = self.getLine(0)
+ line = self.getLogicalLine(self.getStartLine())
+ match = baseClassesRE.search(line)
+ if match:
+ return [s.strip()for s in match.group(1).split(",")]
+ else:
+ return []
+
+ def getColumnOfName(self):
+ match = classNameRE.match(self.getLine(0))
+ return match.start(1)
+
+ def __repr__(self):
+ return "<bike:Class:%s>" % self.name
+
+ def __str__(self):
+ return "bike:Class:"+self.filename+":"+\
+ str(self.getStartLine())+":"+self.name
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Class) and \
+ node.name == self.name
+
+ def __eq__(self,other):
+ return isinstance(other,Class) and \
+ self.filename == other.filename and \
+ self.getStartLine() == other.getStartLine()
+
+# describes an instance of a class
+class Instance:
+ def __init__(self, type):
+ assert type is not None
+ self._type = type
+
+ def getType(self):
+ return self._type
+
+ def __str__(self):
+ return "Instance(%s)"%(self.getType())
+
+
+class Function(StructuralNode, Node):
+ def __init__(self, name, filename, module, linenum, indent,
+ srclines, maskedsrc):
+ StructuralNode.__init__(self, filename, srclines, maskedsrc)
+ Node.__init__(self, name, module, linenum, indent)
+ self.type = "Function"
+
+ def getColumnOfName(self):
+ match = fnNameRE.match(self.getLine(0))
+ return match.start(1)
+
+ def __repr__(self):
+ return "<bike:Function:%s>" % self.name
+
+ def __str__(self):
+ return "bike:Function:"+self.filename+":"+\
+ str(self.getStartLine())+":"+self.name
+
+ def matchesCompilerNode(self,node):
+ return isinstance(node,compiler.ast.Function) and \
+ node.name == self.name
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/load.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+from bike.globals import *
+import os
+from bike.parsing.fastparser import fastparser
+
+class Cache:
+ def __init__(self):
+ self.reset()
+ def reset(self):
+ self.srcnodecache = {}
+ self.typecache = {}
+ self.maskedlinescache = {}
+
+ instance = None
+
+Cache.instance = Cache()
+
+class CantLocateSourceNodeException(Exception): pass
+
+def getSourceNode(filename_path):
+ #print "getSourceNode:",filename_path
+ sourcenode = None
+
+ try:
+ sourcenode = Cache.instance.srcnodecache[filename_path]
+ except KeyError:
+ pass
+
+ if sourcenode is None:
+ from bike.parsing.newstuff import translateFnameToModuleName
+ sourcenode = SourceFile.createFromFile(filename_path,
+ translateFnameToModuleName(filename_path))
+ if sourcenode is None:
+ raise CantLocateSourceNodeException(filename_path)
+
+ Cache.instance.srcnodecache[filename_path]=sourcenode
+ return sourcenode
+
+class SourceFile:
+
+ def createFromString(filename, modulename, src):
+ return SourceFile(filename,modulename,src)
+ createFromString = staticmethod(createFromString)
+
+ def createFromFile(filename,modulename):
+ try:
+ f = file(filename)
+ src = f.read()
+ f.close()
+ except IOError:
+ return None
+ else:
+ return SourceFile(filename,modulename,src)
+
+ createFromFile = staticmethod(createFromFile)
+
+ def __init__(self, filename, modulename, src):
+
+ if os.path.isabs(filename):
+ self.filename = filename
+ else:
+ self.filename = os.path.abspath(filename)
+ self.modulename = modulename
+
+ self.resetWithSource(src)
+
+ def resetWithSource(self, source):
+ # fastparser ast
+ self.fastparseroot = fastparser(source,self.modulename,self.filename)
+ self.fastparseroot.setSourceNode(self)
+ self._lines = source.splitlines(1)
+ self.sourcenode = self
+
+ def __repr__(self):
+ return "Source(%s,%s)"%('source', self.filename)
+
+ def getChildNodes(self):
+ return self.fastparseroot.getChildNodes()
+
+ def getSource(self):
+ return "".join(self.getLines())
+
+ def getLine(self,linenum):
+ return self.getLines()[linenum-1]
+
+ # TODO: rename me!
+ def getFlattenedListOfFastParserASTNodes(self):
+ return self.fastparseroot.getFlattenedListOfChildNodes()
+
+ def getLines(self):
+ return self._lines
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/newstuff.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+from __future__ import generators
+# Holding module for scaffolding needed to transition parsing package
+# into stateless design
+import os
+import re
+from bike.parsing.pathutils import getRootDirectory, getPackageBaseDirectory, \
+ filenameToModulePath, getPathOfModuleOrPackage, getFilesForName
+from bike.parsing.fastparserast import Module, Package, getRoot, getPackage, getModule
+import sys
+from bike.parsing.load import getSourceNode, CantLocateSourceNodeException
+
+
+def translateFnameToModuleName(filename_path):
+ return filenameToModulePath(filename_path)
+
+
+
+# scope is the scope to search from
+def getModuleOrPackageUsingFQN(fqn, dirpath=None):
+ pythonpath = getPythonPath()
+ #print "getModuleOrPackageUsingFQN",pythonpath,fqn
+ if dirpath is not None:
+ assert os.path.isdir(dirpath)
+ pythonpath = [dirpath] + pythonpath
+ filename = getPathOfModuleOrPackage(fqn,pythonpath)
+ #print "getModuleOrPackageUsingFQN - filename",filename
+ if filename is not None:
+ if os.path.isdir(filename):
+ return getPackage(filename)
+ else:
+ return getModule(filename)
+ else:
+ return None
+
+def getPythonPath():
+ return getRoot().pythonpath
+
+
+def generateModuleFilenamesInPythonPath(contextFilename):
+ files = []
+ rootdir = getRootDirectory(contextFilename)
+ if rootdir in getPythonPath():
+ # just search the pythonpath
+ for path in getPythonPath():
+ for file in getFilesForName(path):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+ else:
+ # search the package hierarchy containing contextFilename
+ # in addition to pythonpath
+ basedir = getPackageBaseDirectory(contextFilename)
+ for path in [basedir] + getPythonPath():
+ for file in getFilesForName(path):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+
+ # and search the files immediately above the package hierarchy
+ for file in getFilesForName(os.path.join(rootdir,"*.py")):
+ if file not in files: # check for duplicates
+ files.append(file)
+ yield file
+
+def generateModuleFilenamesInPackage(filenameInPackage):
+ basedir = getPackageBaseDirectory(filenameInPackage)
+ for file in getFilesForName(basedir):
+ yield file
+
+
+
+# search all sourcenodes globally from the perspective of file 'contextFilename'
+def getSourceNodesContainingRegex(regexstr,contextFilename):
+ regex = re.compile(regexstr)
+ for fname in generateModuleFilenamesInPythonPath(contextFilename):
+ try:
+ f = file(fname)
+ src = f.read()
+ finally:
+ f.close()
+ if regex.search(src) is not None:
+ yield getSourceNode(fname)
+
+
+
+
+fromRegex = re.compile("^\s*from\s+(\w+)\s+import")
+importregex = re.compile("^\s*import\s+(\w+)")
+
+# fileInPackage is the filename of a file in the package hierarchy
+# generates file and directory paths
+def generatePackageDependencies(fileInPackage):
+ rejectPackagePaths = [getPackageBaseDirectory(fileInPackage)]
+ for fname in generateModuleFilenamesInPackage(fileInPackage):
+
+ try:
+ f = file(fname)
+ src = f.read()
+ finally:
+ f.close()
+
+ packagepath = None
+
+ for line in src.splitlines():
+ match = fromRegex.search(line) or importregex.search(line)
+ if match is not None:
+ modulepath = match.group(1)
+ packagename = modulepath.split('.')[0]
+ packagepath = getPathOfModuleOrPackage(packagename,
+ getPythonPath())
+ if packagepath is not None and \
+ packagepath not in rejectPackagePaths:
+ rejectPackagePaths.append(packagepath) # avoid duplicates
+ yield packagepath
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/parserutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,194 @@
+from __future__ import generators
+import re
+
+escapedQuotesRE = re.compile(r"(\\\\|\\\"|\\\')")
+
+# changess \" \' and \\ into ** so that text searches
+# for " and ' won't hit escaped ones
+def maskEscapedQuotes(src):
+ return escapedQuotesRE.sub("**", src)
+
+stringsAndCommentsRE = \
+ re.compile("(\"\"\".*?\"\"\"|'''.*?'''|\"[^\"]*\"|\'[^\']*\'|#.*?\n)", re.DOTALL)
+
+import string
+#transtable = string.maketrans('classdefifforwhiletry', "*********************")
+
+# performs a transformation on all of the comments and strings so that
+# text searches for python keywords won't accidently find a keyword in
+# a string or comment
+def maskPythonKeywordsInStringsAndComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ allstrings[i] = allstrings[i].upper()
+ #allstrings[i] = allstrings[i].translate(transtable)
+ return "".join(allstrings)
+
+
+allchars = string.maketrans("", "")
+allcharsExceptNewline = allchars[: allchars.index('\n')]+allchars[allchars.index('\n')+1:]
+allcharsExceptNewlineTranstable = string.maketrans(allcharsExceptNewline, '*'*len(allcharsExceptNewline))
+
+
+# replaces all chars in a string or a comment with * (except newlines).
+# this ensures that text searches don't mistake comments for keywords, and that all
+# matches are in the same line/comment as the original
+def maskStringsAndComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'):
+ allstrings[i] = allstrings[i][:3]+ \
+ allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-3:]
+ else:
+ allstrings[i] = allstrings[i][0]+ \
+ allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-1]
+
+ return "".join(allstrings)
+
+
+# replaces all chars in a string or a comment with * (except newlines).
+# this ensures that text searches don't mistake comments for keywords, and that all
+# matches are in the same line/comment as the original
+def maskStringsAndRemoveComments(src):
+ src = escapedQuotesRE.sub("**", src)
+ allstrings = stringsAndCommentsRE.split(src)
+ # every odd element is a string or comment
+ for i in xrange(1, len(allstrings), 2):
+ if allstrings[i].startswith("'''")or allstrings[i].startswith('"""'):
+ allstrings[i] = allstrings[i][:3]+ \
+ allstrings[i][3:-3].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-3:]
+ elif allstrings[i].startswith("#"):
+ allstrings[i] = '\n'
+ else:
+ allstrings[i] = allstrings[i][0]+ \
+ allstrings[i][1:-1].translate(allcharsExceptNewlineTranstable)+ \
+ allstrings[i][-1]
+ return "".join(allstrings)
+
+
+implicitContinuationChars = (('(', ')'), ('[', ']'), ('{', '}'))
+emptyHangingBraces = [0,0,0,0,0]
+linecontinueRE = re.compile(r"\\\s*(#.*)?$")
+multiLineStringsRE = \
+ re.compile("(^.*?\"\"\".*?\"\"\".*?$|^.*?'''.*?'''.*?$)", re.DOTALL)
+
+#def splitLogicalLines(src):
+# src = multiLineStringsRE.split(src)
+
+# splits the string into logical lines. This requires the comments to
+# be removed, and strings masked (see other fns in this module)
+def splitLogicalLines(src):
+ physicallines = src.splitlines(1)
+ return [x for x in generateLogicalLines(physicallines)]
+
+
+class UnbalancedBracesException: pass
+
+# splits the string into logical lines. This requires the strings
+# masked (see other fns in this module)
+# Physical Lines *Must* start on a non-continued non-in-a-comment line
+# (although detects unbalanced braces)
+def generateLogicalLines(physicallines):
+ tmp = []
+ hangingBraces = list(emptyHangingBraces)
+ hangingComments = 0
+ for line in physicallines:
+ # update hanging braces
+ for i in range(len(implicitContinuationChars)):
+ contchar = implicitContinuationChars[i]
+ numHanging = hangingBraces[i]
+ hangingBraces[i] = numHanging+line.count(contchar[0]) - \
+ line.count(contchar[1])
+
+ hangingComments ^= line.count('"""') % 2
+ hangingComments ^= line.count("'''") % 2
+
+ if hangingBraces[0] < 0 or \
+ hangingBraces[1] < 0 or \
+ hangingBraces[2] < 0:
+ raise UnbalancedBracesException()
+
+ if linecontinueRE.search(line):
+ tmp.append(line)
+ elif hangingBraces != emptyHangingBraces:
+ tmp.append(line)
+ elif hangingComments:
+ tmp.append(line)
+ else:
+ tmp.append(line)
+ yield "".join(tmp)
+ tmp = []
+
+
+# see above but yields (line,linenum)
+# needs physicallines to have linenum attribute
+# TODO: refactor with previous function
+def generateLogicalLinesAndLineNumbers(physicallines):
+ tmp = []
+ hangingBraces = list(emptyHangingBraces)
+ hangingComments = 0
+ linenum = None
+ for line in physicallines:
+ if tmp == []:
+ linenum = line.linenum
+
+ # update hanging braces
+ for i in range(len(implicitContinuationChars)):
+ contchar = implicitContinuationChars[i]
+ numHanging = hangingBraces[i]
+ hangingBraces[i] = numHanging+line.count(contchar[0]) - \
+ line.count(contchar[1])
+
+ hangingComments ^= line.count('"""') % 2
+ hangingComments ^= line.count("'''") % 2
+
+ if linecontinueRE.search(line):
+ tmp.append(line)
+ elif hangingBraces != emptyHangingBraces:
+ tmp.append(line)
+ elif hangingComments:
+ tmp.append(line)
+ else:
+ tmp.append(line)
+ yield "".join(tmp),linenum
+ tmp = []
+
+
+
+
+# takes a line of code, and decorates it with noops so that it can be
+# parsed by the python compiler.
+# e.g. "if foo:" -> "if foo: pass"
+# returns the line, and the adjustment made to the column pos of the first char
+# line must have strings and comments masked
+#
+# N.B. it only inserts keywords whitespace and 0's
+notSpaceRE = re.compile("\s*(\S)")
+commentRE = re.compile("#.*$")
+
+def makeLineParseable(line):
+ return makeLineParseableWhenCommentsRemoved(commentRE.sub("",line))
+
+def makeLineParseableWhenCommentsRemoved(line):
+ line = line.strip()
+ if ":" in line:
+ if line.endswith(":"):
+ line += " pass"
+ if line.startswith("try"):
+ line += "\nexcept: pass"
+ elif line.startswith("except") or line.startswith("finally"):
+ line = "try: pass\n" + line
+ return line
+ elif line.startswith("else") or line.startswith("elif"):
+ line = "if 0: pass\n" + line
+ return line
+ elif line.startswith("yield"):
+ return ("return"+line[5:])
+ return line
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/pathutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,165 @@
+
+# A some of this code is take from Pythius -
+# Copyright (GPL) 2001 Jurgen Hermann <jh@web.de>
+
+from bike.globals import *
+import os
+
+def containsAny(str, set):
+ """ Check whether 'str' contains ANY of the chars in 'set'
+ """
+ return 1 in [c in str for c in set]
+
+
+
+def getPathOfModuleOrPackage(dotted_name, pathlist = None):
+ """ Get the filesystem path for a module or a package.
+
+ Return the file system path to a file for a module,
+ and to a directory for a package. Return None if
+ the name is not found, or is a builtin or extension module.
+ """
+ from bike.parsing.newstuff import getPythonPath
+ if pathlist is None:
+ pathlist = getPythonPath()
+
+ import imp
+
+ # split off top-most name
+ parts = dotted_name.split('.', 1)
+
+ if len(parts) > 1:
+ # we have a dotted path, import top-level package
+ try:
+ file, pathname, description = imp.find_module(parts[0], pathlist)
+ if file: file.close()
+ except ImportError:
+ return None
+
+ # check if it's indeed a package
+ if description[2] == imp.PKG_DIRECTORY:
+ # recursively handle the remaining name parts
+ pathname = getPathOfModuleOrPackage(parts[1], [pathname])
+ else:
+ pathname = None
+ else:
+ # plain name
+ try:
+ file, pathname, description = imp.find_module(dotted_name, pathlist)
+ if file: file.close()
+ if description[2]not in[imp.PY_SOURCE, imp.PKG_DIRECTORY]:
+ pathname = None
+ except ImportError:
+ pathname = None
+
+ return pathname
+
+
+def getFilesForName(name):
+ """ Get a list of module files for a filename, a module or package name,
+ or a directory.
+ """
+ import imp
+
+ if not os.path.exists(name):
+ # check for glob chars
+ if containsAny(name, "*?[]"):
+ import glob
+ files = glob.glob(name)
+ list = []
+ for file in files:
+ list.extend(getFilesForName(file))
+ return list
+
+ # try to find module or package
+ name = getPathOfModuleOrPackage(name)
+ if not name:
+ return[]
+
+ if os.path.isdir(name):
+ # find all python files in directory
+ list = []
+ os.path.walk(name, _visit_pyfiles, list)
+ return list
+ elif os.path.exists(name) and not name.startswith("."):
+ # a single file
+ return [name]
+
+ return []
+
+def _visit_pyfiles(list, dirname, names):
+ """ Helper for getFilesForName().
+ """
+ # get extension for python source files
+ if not globals().has_key('_py_ext'):
+ import imp
+ global _py_ext
+ _py_ext = [triple[0]for triple in imp.get_suffixes()if triple[2] == imp.PY_SOURCE][0]
+
+ # don't recurse into CVS or Subversion directories
+ if 'CVS'in names:
+ names.remove('CVS')
+ if '.svn'in names:
+ names.remove('.svn')
+
+ names_copy = [] + names
+ for n in names_copy:
+ if os.path.isdir(os.path.join(dirname, n))and \
+ not os.path.exists(os.path.join(dirname, n, "__init__.py")):
+ names.remove(n)
+
+ # add all *.py files to list
+ list.extend(
+ [os.path.join(dirname, file)
+ for file in names
+ if os.path.splitext(file)[1] == _py_ext and not file.startswith(".")])
+
+
+# returns the directory which holds the first package of the package
+# hierarchy under which 'filename' belongs
+def getRootDirectory(filename):
+ if os.path.isdir(filename):
+ dir = filename
+ else:
+ dir = os.path.dirname(filename)
+ while dir != "" and \
+ os.path.exists(os.path.join(dir, "__init__.py")):
+ dir = os.path.dirname(dir)
+ return dir
+
+
+
+# Returns the root package directoryname of the package hierarchy
+# under which 'filename' belongs
+def getPackageBaseDirectory(filename):
+ if os.path.isdir(filename):
+ dir = filename
+ else:
+ dir = os.path.dirname(filename)
+
+ if not os.path.exists(os.path.join(dir, "__init__.py")):
+ # parent dir is not a package
+ return dir
+
+ while dir != "" and \
+ os.path.exists(os.path.join(os.path.dirname(dir), "__init__.py")):
+ dir = os.path.dirname(dir)
+ return dir
+
+
+
+def filenameToModulePath(fname):
+ directoriesPreceedingRoot = getRootDirectory(fname)
+ import os
+ # strip off directories preceeding root package directory
+ if directoriesPreceedingRoot != "":
+ mpath = fname.replace(directoriesPreceedingRoot, "")
+ else:
+ mpath = fname
+
+ if(mpath[0] == os.path.normpath("/")):
+ mpath = mpath[1:]
+ mpath, ext = os.path.splitext(mpath)
+ mpath = mpath.replace(os.path.normpath("/"), ".")
+ return mpath
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("../..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath("../.."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_fastparser.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+import unittest
+from fastparser import*
+from bike.parsing.load import*
+from bike.parsing.fastparserast import*
+from bike.testutils import *
+
+class TestFastParser(BRMTestCase):
+ def test_doesntGetClassDeclsInMLStrings(self):
+ src = trimLines('''
+ """
+ class foo bah
+ """
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() == []
+
+ def test_evaluatesMLStringWithQuoteInIt(self):
+ src = trimLines('''
+ """some ml comment inclosing a " """
+ def foo:
+ pass
+ " hello "
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+ def test_handlesClassDefsWithTwoSpacesInDecl(self):
+ src = trimLines('''
+ class foo: pass
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+ def test_handlesFnDefsWithTwoSpacesInDecl(self):
+ src = trimLines('''
+ def foo: pass
+ ''')
+ root = fastparser(src)
+ assert root.getChildNodes() != []
+
+
+MLStringWithQuoteInIt = """
+\"\"\"some ml comment inclosing a \" \"\"\"
+def foo:
+ pass
+\" hello \"
+"""
+
+def load(path):
+ files = getFilesForName(path)
+ for fname in files:
+ src = file(fname).read()
+ fastparser(src)
+ #print fname
+ #myroot = parseFile(fname)
+
+
+
+def fastparsetreeToString(root):
+ class stringholder: pass
+ s = stringholder()
+ s.mystr = ""
+ s.tabstr = ""
+ def t2s(node):
+ if isinstance(node, Class):
+ s.mystr+=s.tabstr+"class "+node.name+"\n"
+ s.tabstr+="\t"
+ for n in node.getChildNodes():
+ t2s(n)
+ s.tabstr = s.tabstr[:-1]
+
+ elif isinstance(node, Function):
+ s.mystr+=s.tabstr+"function "+node.name+"\n"
+ s.tabstr+="\t"
+ for n in node.getChildNodes():
+ t2s(n)
+ s.tabstr = s.tabstr[:-1]
+
+
+ for n in root.getChildNodes():
+ t2s(n)
+ return s.mystr
+
+
+def compilerParseTreeToString(root):
+ try:
+ class TreeVisitor:
+ def __init__(self):
+ self.mystr = ""
+ self.tabstr = ""
+
+ def visitClass(self, node):
+ self.mystr+=self.tabstr+"class "+node.name+"\n"
+ self.tabstr+="\t"
+ for child in node.getChildNodes():
+ self.visit(child)
+ self.tabstr = self.tabstr[:-1]
+
+ def visitFunction(self, node):
+ self.mystr+=self.tabstr+"function "+node.name+"\n"
+ self.tabstr+="\t"
+ for child in node.getChildNodes():
+ self.visit(child)
+ self.tabstr = self.tabstr[:-1]
+
+ return compiler.walk(root, TreeVisitor()).mystr
+
+ except:
+ log.exception("ex")
+ import sys
+ sys.exit(0)
+
+
+def compareCompilerWithFastparserOverPath(path):
+ from bike.parsing.load import getFilesForName
+ files = getFilesForName(path)
+ for fname in files:
+ if fname.endswith("bdist_wininst.py"): continue
+ log.info(fname)
+ src = file(fname).read()
+ try:
+ compiler_root = compiler.parse(src)
+ except SyntaxError:
+ continue
+ fastparse_root = fastparser(src)
+ str1 = fastparsetreeToString(fastparse_root)
+ str2 = compilerParseTreeToString(compiler_root)
+ assert str1 == str2, "\n"+"-"*70+"\n"+str1+"-"*70+"\n"+str2
+
+
+def timeParseOfPythonLibrary(path):
+ import time
+ t1 = time.time()
+ files = getFilesForName(path)
+ import sys
+ for fname in files:
+ if fname.endswith("bdist_wininst.py"): continue
+ src = file(fname).read()
+ fastparser(src)
+ print "\n", time.time()-t1
+
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.INFO)
+ # add soak tests to end of test
+ class Z_SoakTestFastparser(BRMTestCase):
+
+ def test_A_timeParseOfPythonLibrary(self):
+ timeParseOfPythonLibrary("/usr/local/lib/python2.2")
+
+ def test_parsesPythonLibraryCorrectly(self):
+ print ""
+ compareCompilerWithFastparserOverPath("/usr/local/lib/python2.2")
+
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_fastparserast.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+import unittest
+from fastparserast import *
+from fastparser import fastparser
+from bike.query.getTypeOf import getTypeOf
+from bike.testutils import *
+
+class TestGetModule(BRMTestCase):
+
+ def test_getRootWorksAfterDefinedByCreateSourceNodeAt(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ assert root == getRoot()
+
+ def test_returnsNoneIfModuleDoesntExist(self):
+ assert getModule(tmpfile) == None
+
+
+class TestGetEndLine(BRMTestCase):
+ def test_returnsEndLineWithSimpleFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ root = fastparser(src)
+ fn = getTypeOf(root,"foo")
+ self.assertEqual(fn.getEndLine(),7)
+
+ def test_worksWithFunctionsThatHaveEmptyLinesInThem(self):
+ src = fnWithEmptyLineInIt
+ root = fastparser(src)
+ fn = getTypeOf(root,"TheClass.theFunction")
+ self.assertEqual(fn.getEndLine(),8)
+
+class TestGetBaseClassNames(BRMTestCase):
+ def test_worksForClassHierarchy(self):
+ src = trimLines("""
+ class root:
+ def theMethod():
+ pass
+
+ class a(root):
+ def theMethod():
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(a,b):
+ def theMethod():
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+ #classes = getASTNodeFromSrc(src,"Source").fastparseroot.getChildNodes()
+ classes = createAST(src).fastparseroot.getChildNodes()
+ self.assertEqual(classes[3].getBaseClassNames(),['a','b'])
+
+ def test_returnsEmptyListForClassWithNoBases(self):
+ src = trimLines("""
+ class root:
+ pass
+ """)
+ #classes = getASTNodeFromSrc(src,"Source").fastparseroot.getChildNodes()
+ classes = createAST(src).fastparseroot.getChildNodes()
+ self.assertEqual(classes[0].getBaseClassNames(),[])
+
+
+class TestGetMaskedLines(BRMTestCase):
+ def test_doit(self):
+ src =trimLines("""
+ class foo: #bah
+ pass
+ """)
+ mod = createAST(src).fastparseroot
+ lines = mod.getMaskedModuleLines()
+ assert lines[0] == "class foo: #***\n"
+
+
+class TestGetLinesNotIncludingThoseBelongingToChildScopes(BRMTestCase):
+ def test_worksForModule(self):
+ src =trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ mod = createAST(src).fastparseroot
+ self.assertEqual(''.join(mod.getLinesNotIncludingThoseBelongingToChildScopes()),
+ trimLines("""
+ a = foo()
+ a.theMethod()
+ """))
+
+ def test_worksForModuleWithSingleLineFunctions(self):
+ src=trimLines("""
+ a = blah()
+ def foo(): pass
+ b = 1
+ """)
+ mod = createAST(src).fastparseroot
+ lines = mod.getLinesNotIncludingThoseBelongingToChildScopes()
+ self.assertEqual(''.join(lines),
+ trimLines("""
+ a = blah()
+ b = 1
+ """))
+
+
+ def test_worksForSingleLineFunction(self):
+ src=trimLines("""
+ a = blah()
+ def foo(): pass
+ b = 1
+ """)
+ fn = createAST(src).fastparseroot.getChildNodes()[0]
+ lines = fn.getLinesNotIncludingThoseBelongingToChildScopes()
+ self.assertEqual(''.join(lines),
+ trimLines("""
+ def foo(): pass
+ """))
+
+
+fnWithEmptyLineInIt = """
+class TheClass:
+ def theFunction():
+ a = foo()
+
+ print 'a'
+
+ # end of function
+"""
+
+if __name__ == "__main__":
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_load.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import compiler
+import os
+
+from bike import testdata
+from bike.testutils import *
+from bike.mock import Mock
+
+from pathutils import getPathOfModuleOrPackage
+from load import *
+import load as loadmodule
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_newstuff.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,284 @@
+#!/usr/bin/env python
+import setpath
+import os
+import unittest
+from bike.testutils import *
+from bike.parsing.fastparserast import getRoot
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN,\
+ generateModuleFilenamesInPythonPath, getSourceNodesContainingRegex,\
+ generatePackageDependencies
+
+
+class TestGetModuleOrPackageUsingFQN(BRMTestCase):
+ def test_worksForFullPath(self):
+ try:
+ createPackageStructure("pass","pass")
+ self.assertEqual(getModuleOrPackageUsingFQN("a.b.bah").filename,
+ pkgstructureFile2)
+ finally:
+ removePackageStructure()
+
+ def test_worksForPackage(self):
+ try:
+ createPackageStructure("pass","pass")
+ self.assertEqual(getModuleOrPackageUsingFQN("a.b").path,
+ pkgstructureChilddir)
+ finally:
+ removePackageStructure()
+
+
+class TestGenerateModuleFilenamesInPythonPath(BRMTestCase):
+ def test_works(self):
+ try:
+ createPackageStructure("pass","pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+
+
+ assert os.path.join(pkgstructureBasedir,"__init__.py") in fnames
+ assert pkgstructureFile1 in fnames
+ assert os.path.join(pkgstructureChilddir,"__init__.py") in fnames
+ assert pkgstructureFile2 in fnames
+ assert len(fnames) == 5
+ finally:
+ removePackageStructure()
+
+
+
+ def test_doesntTraverseIntoNonPackages(self):
+ try:
+ createPackageStructure("pass","pass")
+ nonPkgDir = os.path.join(pkgstructureChilddir,"c")
+ newfile = os.path.join(nonPkgDir,"baz.py")
+ # N.B. don't put an __init__.py in it, so isnt a package
+ os.makedirs(nonPkgDir)
+ writeFile(newfile,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert newfile not in fnames
+ finally:
+ #os.remove(initfile)
+ os.remove(newfile)
+ os.removedirs(nonPkgDir)
+ removePackageStructure()
+
+
+ def test_doesScanFilesInTheRootDirectory(self):
+ try:
+ createPackageStructure("pass","pass","pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert pkgstructureFile0 in fnames
+ finally:
+ #os.remove(initfile)
+ removePackageStructure()
+
+ def test_returnsOtherFilesInSameNonPackageDirectory(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ writeTmpTestFile("")
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, "")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(tmpfile)]
+ assert newtmpfile in fnames
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+ getRoot().pythonpath = oldpath
+
+
+
+ def test_doesntTraverseIntoNonPackagesUnderRoot(self):
+ try:
+ os.makedirs(pkgstructureBasedir)
+ writeFile(pkgstructureFile1,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ assert pkgstructureFile1 not in fnames
+ finally:
+ os.remove(pkgstructureFile1)
+ os.removedirs(pkgstructureBasedir)
+
+
+ def test_doesntGenerateFilenamesMoreThanOnce(self):
+ try:
+ createPackageStructure("pass","pass")
+ newfile = os.path.join(pkgstructureChilddir,"baz.py")
+ writeFile(newfile,"pass")
+ fnames = [f for f in \
+ generateModuleFilenamesInPythonPath(pkgstructureFile2)]
+ matched = [f for f in fnames if f == newfile]
+ self.assertEqual(1, len(matched))
+ finally:
+ os.remove(newfile)
+ removePackageStructure()
+
+class TestGetSourceNodesContainingRegex(BRMTestCase):
+ def test_works(self):
+ try:
+ createPackageStructure("# testregexfoobah","pass")
+ srcfiles = [s for s in
+ getSourceNodesContainingRegex("testregexfoobah",
+ pkgstructureFile2)]
+ self.assertEqual(pkgstructureFile1,srcfiles[0].filename)
+ finally:
+ removePackageStructure()
+
+class TestGenerateModuleFilenamesInPythonPath2(BRMTestCase):
+ def test_getsAllFilenamesInSameHierarchyAsContextFile(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile1)]
+ self.assert_(pkgstructureFile0 in fnames)
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ removePackageStructure()
+
+ def test_getsFilenamesInSubPackagesIfCtxFilenameIsInTheRoot(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile0)]
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ removePackageStructure()
+
+ def test_doesntTraverseOtherPackagesOffOfTheRoot(self):
+ try:
+ oldpath = getRoot().pythonpath
+ getRoot().pythonpath = [] # clear the python path
+ createPackageStructure("","")
+ os.makedirs(os.path.join(pkgstructureRootDir, "c"))
+ writeFile(os.path.join(pkgstructureRootDir, "c", "__init__.py"), "# ")
+ bazfile = os.path.join(pkgstructureRootDir, "c", "baz.py")
+ writeFile(bazfile, "pass")
+ fnames = [f for f in
+ generateModuleFilenamesInPythonPath(pkgstructureFile1)]
+ self.assert_(pkgstructureFile0 in fnames)
+ self.assert_(pkgstructureFile1 in fnames)
+ self.assert_(pkgstructureFile2 in fnames)
+ self.assert_(bazfile not in fnames)
+ finally:
+ getRoot().pythonpath = oldpath
+ os.remove(os.path.join(pkgstructureRootDir, "c", "baz.py"))
+ os.remove(os.path.join(pkgstructureRootDir, "c", "__init__.py"))
+ os.removedirs(os.path.join(pkgstructureRootDir, "c"))
+ removePackageStructure()
+
+
+class TestGetPackageDependencies(BRMTestCase):
+
+ def test_followsImportModule(self):
+ try:
+ createPackageStructure("","import c.bing")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_followsFromImportPackage(self):
+ try:
+ createPackageStructure("","import c")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+
+ def test_followsFromImportStar(self):
+ try:
+ createPackageStructure("","from c import *")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+ def test_followsFromImportModule(self):
+ try:
+ createPackageStructure("","from c import bing")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([pkgstructureBasedir2],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntBreakIfImportIsInAMultilineString(self):
+ try:
+ createPackageStructure("",trimLines("""
+ '''
+ from aoeuaoeu import aocxaoieicxoe
+ '''
+ """))
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+ def test_doesntBreakIfImportIsCommented(self):
+ try:
+ createPackageStructure("","#from aoeuaoeu import aocxaoieicxoe")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntBreakIfCantFindImport(self):
+ try:
+ createPackageStructure("","from aoeuaoeu import aocxaoieicxoe")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+ def test_doesntIncludeCurrentPackage(self):
+ try:
+ createPackageStructure("","import a.foo")
+ createSecondPackageStructure("")
+ dependencies = [d for d in
+ generatePackageDependencies(pkgstructureFile2)]
+ self.assertEqual([],dependencies)
+ finally:
+ removeSecondPackageStructure()
+ removePackageStructure()
+
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_parserutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+import unittest
+from parserutils import *
+from bike.testutils import *
+
+class TestRemoveEscapedQuotes(BRMTestCase):
+
+ def testMaskEscapedQuotes_MasksEscapedQuotes(self):
+ src = '\" \\\\\\\" \' \\\\\\\\\" \' \''
+ self.assertEqual(maskEscapedQuotes(src),'" **** \' ****" \' \'')
+
+class TestMungePythonKeywordsInStrings(BRMTestCase):
+ def test_mungesKeywords(self):
+ src = '\"\"\"class try while\"\"\" class2 try2 while2 \'\'\' def if for \'\'\' def2 if2 for2'
+ self.assertEqual(maskPythonKeywordsInStringsAndComments(src),
+ '"""CLASS TRY WHILE""" class2 try2 while2 \'\'\' DEF IF FOR \'\'\' def2 if2 for2')
+
+
+class TestSplitLines(BRMTestCase):
+ def test_handlesExplicitlyContinuedLineWithComment(self):
+ self.assertEqual(splitLogicalLines(explicitlyContinuedLineWithComment),
+ ['\n', 'z = a + b + \\ # comment\n c + d\n', 'pass\n'])
+
+ def test_handlesImplicitlyContinuedLine(self):
+ self.assertEqual(splitLogicalLines(implicitlyContinuedLine),
+ ['\n', 'z = a + b + (\n c + d)\n', 'pass\n'])
+
+ def test_handlesNestedImplicitlyContinuedLine(self):
+ self.assertEqual(splitLogicalLines(implicitlyContinuedLine2),
+ ['\n', 'z = a + b + ( c + [d\n + e]\n + f) # comment\n', 'pass\n'])
+
+
+ def test_handlesMultiLineStrings(self):
+ self.assertEqual(splitLogicalLines(multilineComment),
+ ['\n', "''' this is an mlc\nso is this\n'''\n", 'pass\n'])
+
+
+class TestMakeLineParseable(BRMTestCase):
+ def test_worksWithIfStatement(self):
+ src = "if foo:"
+ self.assertEqual(makeLineParseable(src),("if foo: pass"))
+
+ def test_worksWithTryStatement(self):
+ src = "try :"
+ self.assertEqual(makeLineParseable(src),("try : pass\nexcept: pass"))
+
+ def test_worksOnTryStatementWithCodeInlined(self):
+ src = "try : a = 1"
+ self.assertEqual(makeLineParseable(src),("try : a = 1\nexcept: pass"))
+
+ def test_worksWithExceptStatement(self):
+ src = "except :"
+ self.assertEqual(makeLineParseable(src),("try: pass\nexcept : pass"))
+
+ def test_worksWithFinallyStatement(self):
+ src = "finally:"
+ self.assertEqual(makeLineParseable(src),("try: pass\nfinally: pass"))
+
+ def test_worksWithIfStatement(self):
+ src = "if foo:"
+ self.assertEqual(makeLineParseable(src),("if foo: pass"))
+
+ def test_worksWithElseStatement(self):
+ src = "else :"
+ self.assertEqual(makeLineParseable(src),("if 0: pass\nelse : pass"))
+
+ def test_worksWithElifStatement(self):
+ src = "elif foo:"
+ self.assertEqual(makeLineParseable(src),("if 0: pass\nelif foo: pass"))
+
+
+def runOverPath(path):
+ import compiler
+ from parser import ParserError
+ from bike.parsing.load import getFilesForName
+ files = getFilesForName(path)
+ for fname in files:
+ print fname
+ src = file(fname).read()
+ #print src
+ src = maskStringsAndRemoveComments(src)
+
+ for logicalline in splitLogicalLines(src):
+ #print "logicalline=",logicalline
+ logicalline = logicalline.strip()
+ logicalline = makeLineParseable(logicalline)
+ try:
+ compiler.parse(logicalline)
+ except ParserError:
+ print "ParserError on logicalline:",logicalline
+ except:
+ log.exception("caught exception")
+
+
+explicitlyContinuedLineWithComment = """
+z = a + b + \ # comment
+ c + d
+pass
+"""
+
+implicitlyContinuedLine = """
+z = a + b + (
+ c + d)
+pass
+"""
+
+
+implicitlyContinuedLine2 = """
+z = a + b + ( c + [d
+ + e]
+ + f) # comment
+pass
+"""
+
+multilineComment = """
+''' this is an mlc
+so is this
+'''
+pass
+"""
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.INFO)
+
+ # add soak tests to end of test
+ class Z_SoakTest(BRMTestCase):
+ def test_linesRunThroughPythonParser(self):
+ print ""
+ #print splitLogicalLines(file('/usr/local/lib/python2.2/aifc.py').read())
+ #runOverPath('/usr/local/lib/python2.2/test/badsyntax_nocaret.py')
+ runOverPath('/usr/local/lib/python2.2/')
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_pathutils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import compiler
+import os
+
+from bike import testdata
+from bike.testutils import *
+from bike.mock import Mock
+
+from pathutils import getPathOfModuleOrPackage
+from pathutils import *
+import pathutils as loadmodule
+
+class TestGetFilesForName(BRMTestCase):
+ def testGetFilesForName_recursivelyReturnsFilesInBreadthFirstOrder(self):
+ createPackageStructure("pass", "pass")
+
+ files = getFilesForName(pkgstructureBasedir)
+ for f in files:
+ assert f in \
+ [os.path.join(pkgstructureBasedir, '__init__.py'),
+ os.path.join(pkgstructureBasedir, 'foo.py'),
+ os.path.join(pkgstructureChilddir, '__init__.py'),
+ os.path.join(pkgstructureChilddir, 'bah.py')]
+
+ def testGetFilesForName_globsStars(self):
+ createPackageStructure("pass", "pass")
+ assert getFilesForName(os.path.join(pkgstructureBasedir, "fo*")) == [os.path.join(pkgstructureBasedir, 'foo.py')]
+ removePackageStructure()
+
+ def testGetFilesForName_doesntListFilesWithDotAtFront(self):
+ writeFile(os.path.join(".foobah.py"),"")
+ files = getFilesForName("a")
+ self.assertEqual([],files)
+
+
+
+
+class TestGetRootDirectory(BRMTestCase):
+ def test_returnsParentDirectoryIfFileNotInPackage(self):
+ try:
+ # this doesnt have __init__.py file, so
+ # isnt package
+ os.makedirs("a")
+ writeFile(os.path.join("a", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("a", "foo.py"))
+ assert dir == "a"
+ finally:
+ os.remove(os.path.join("a", "foo.py"))
+ os.removedirs(os.path.join("a"))
+
+ def test_returnsFirstNonPackageParentDirectoryIfFileInPackage(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("root", "a", "b", "foo.py"))
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+ def test_returnsFirstNonPackageParentDirectoryIfPathIsAPackage(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory(os.path.join("root", "a", "b"))
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+ def test_returnsDirIfDirIsTheRootDirectory(self):
+ try:
+ os.makedirs(os.path.join("root", "a", "b"))
+ writeFile(os.path.join("root", "a", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "__init__.py"), "# ")
+ writeFile(os.path.join("root", "a", "b", "foo.py"), "pass")
+ dir = loadmodule.getRootDirectory("root")
+ assert dir == "root"
+ finally:
+ os.remove(os.path.join("root", "a", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "__init__.py"))
+ os.remove(os.path.join("root", "a", "b", "foo.py"))
+ os.removedirs(os.path.join("root", "a", "b"))
+
+
+class getPackageBaseDirectory(BRMTestCase):
+ def test_returnsBasePackageIfFileInPackageHierarchy(self):
+ try:
+ createPackageStructure("","")
+ dir = loadmodule.getPackageBaseDirectory(pkgstructureFile2)
+ self.assertEqual(pkgstructureBasedir, dir)
+ finally:
+ removePackageStructure()
+
+ def test_returnsFileDirectoryIfFileNotInPackage(self):
+ try:
+ createPackageStructure("","")
+ dir = loadmodule.getPackageBaseDirectory(pkgstructureFile0)
+ self.assertEqual(pkgstructureRootDir, dir)
+ finally:
+ removePackageStructure()
+
+
+class TestGetPathOfModuleOrPackage(BRMTestCase):
+ def test_worksForFullPath(self):
+ try:
+ createPackageStructure("pass","pass")
+ import sys
+ self.assertEqual(getPathOfModuleOrPackage("a.b.bah",
+ [pkgstructureRootDir]),
+ pkgstructureFile2)
+ finally:
+ removePackageStructure()
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from utils import *
+
+class Test_CarCdrEtc(unittest.TestCase):
+ def test_carReturnsTheFirstElementOfTheFqn(self):
+ fqn = "apple.pear.foo"
+ assert fqn_car(fqn) == "apple"
+
+ def test_carReturnsElementInOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_car(fqn) == "apple"
+
+ def test_cdrReturnsTheAllElementsOfTheFqnExceptFirst(self):
+ fqn = "apple.pear.foo"
+ assert fqn_cdr(fqn) == "pear.foo"
+
+ def test_cdrReturnsEmptyStringForOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_cdr(fqn) == ""
+
+ def test_rcarReturnsTheLastElementOfTheFqn(self):
+ fqn = "apple.pear.foo"
+ assert fqn_rcar(fqn) == "foo"
+
+ def test_rcarReturnsElementInOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_rcar(fqn) == "apple"
+
+ def test_rdrReturnsTheAllElementsOfTheFqnExceptLast(self):
+ fqn = "apple.pear.foo"
+ assert fqn_rcdr(fqn) == "apple.pear"
+
+ def test_rdrReturnsEmptyStringForOneElementFqn(self):
+ fqn = "apple"
+ assert fqn_rcdr(fqn) == ""
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/test_visitor.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+from __future__ import generators
+import setpath
+import unittest
+import visitor
+from bike.testutils import *
+
+class TestVisitor(BRMTestCase):
+ def test_callsVistorFunctions(self):
+ tree = createTree()
+
+ class TreeVisitor:
+ def __init__(self):
+ self.txt = []
+
+ def visitAClass(self,node):
+ self.txt.append("visitAClass")
+ self.txt.append(node.txt)
+ return self.visitChildren(node)
+
+ def visitCClass(self,node):
+ self.txt.append("visitCClass")
+ self.txt.append(node.txt)
+
+ def getTxt(self):
+ return ",".join(self.txt)
+
+ self.assertEqual(visitor.walk(tree,TreeVisitor()).getTxt(),
+ "visitAClass,aclass,visitCClass,cclass0,visitCClass,cclass1,visitCClass,cclass2")
+
+
+ def test_callsVisitorFunctionsWithYield(self):
+ tree = createTree()
+
+ class TreeVisitor:
+ def __init__(self):
+ self.txt = []
+
+ def visitAClass(self,node):
+ self.txt.append("visitAClass")
+ self.txt.append(node.txt)
+ yield node
+ for i in self.visitChildren(node):
+ yield i
+
+ def visitCClass(self,node):
+ self.txt.append("visitCClass")
+ self.txt.append(node.txt)
+ if 0: yield 1
+
+ def getTxt(self):
+ return ",".join(self.txt)
+
+ for node in visitor.walkAndGenerate(tree,TreeVisitor()):
+ assert node.txt == "aclass"
+
+
+def createTree():
+ n = AClass("aclass")
+ for i in xrange(3):
+ b = n.addChildNode(BClass("bclass%d" % i))
+ for j in xrange(20):
+ b = b.addChildNode(BClass("bclass%d" % i))
+ b.addChildNode(CClass("cclass%d" % i))
+ return n
+
+class node(object):
+ def __init__(self,txt):
+ self._childNodes=[]
+ self.txt = txt
+
+ def addChildNode(self,node):
+ self._childNodes.append(node)
+ return node
+
+ def getChildNodes(self):
+ return [x for x in self._childNodes]
+
+
+class AClass(node):
+ pass
+
+class BClass(node):
+ pass
+
+class CClass(node):
+ pass
+
+if __name__ == "__main__":
+
+ # add perf test at end of tests
+ class Z_SoakTestFastparser(BRMTestCase):
+ def test_parsesPythonLibraryCorrectly(self):
+
+ class TreeVisitor:
+ pass
+
+ import time
+
+ tree = createTree()
+
+ t1 = time.time()
+ for i in xrange(1000):
+ visitor.walk(tree,TreeVisitor())
+ print "tree without yield",time.time()-t1
+
+ t1 = time.time()
+ for i in xrange(1000):
+ for node in visitor.walkAndGenerate(tree,TreeVisitor()):
+ pass
+ print "tree with yield",time.time()-t1
+
+
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import setpath
+
+import unittest
+
+#import all the tests
+from test_load import *
+from test_newstuff import *
+from test_parserutils import *
+from test_fastparser import *
+from test_fastparserast import *
+
+if __name__ == "__main__":
+ from bike import logging
+ logging.init()
+ log = logging.getLogger("bike")
+ log.setLevel(logging.WARN)
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,30 @@
+# get the first element of a fully qualified python path
+#(e.g. _car('a.b.c.d') = 'a')
+def fqn_car(fqn):
+ try:
+ return fqn[:fqn.index(".")]
+ except ValueError: # i.e. no dots in fqn
+ return fqn
+
+# get the other elements of a fully qualified python path
+#(e.g. _cdr('a.b.c.d') = 'b.c.d')
+def fqn_cdr(fqn):
+ try:
+ return fqn[fqn.index(".")+1:]
+ except ValueError: # i.e. no dots in fqn
+ return ""
+
+# reverse of above _rcar("a.b.c.d") = "d"
+def fqn_rcar(fqn):
+ try:
+ return fqn[fqn.rindex(".")+1:]
+ except ValueError: # i.e. no dots in fqn
+ return fqn
+
+
+# reverse of above _rcdr("a.b.c.d") = "a.b.c"
+def fqn_rcdr(fqn):
+ try:
+ return fqn[:fqn.rindex(".")]
+ except ValueError: # i.e. no dots in fqn
+ return ""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/parsing/visitor.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,49 @@
+from __future__ import generators
+
+class TreeWalker(object):
+ VERBOSE = 0
+
+ def __init__(self):
+ self.node = None
+ self._cache = {}
+
+ def default(self, node, *args):
+ for child in node.getChildNodes():
+ self.dispatch(child, *args)
+
+ def dispatch(self, node, *args):
+ self.node = node
+ klass = node.__class__
+ meth = self._cache.get(klass, None)
+ if meth is None:
+ className = klass.__name__
+ meth = getattr(self.visitor, 'visit' + className, self.default)
+ self._cache[klass] = meth
+ return meth(node, *args)
+
+ def preorder(self, tree, visitor, *args):
+ """Do preorder walk of tree using visitor"""
+ self.visitor = visitor
+ visitor.visit = self.dispatch
+ visitor.visitChildren = self.default
+ return self.dispatch(tree, *args)
+
+class GeneratingTreeWalker(TreeWalker):
+
+ def default(self, node, *args):
+ for child in node.getChildNodes():
+ for i in self.dispatch(child, *args):
+ yield i
+
+
+def walk(tree, visitor):
+ walker = TreeWalker()
+ walker.preorder(tree, visitor)
+ return walker.visitor
+
+
+def walkAndGenerate(tree,visitor):
+ walker = GeneratingTreeWalker()
+ return walker.preorder(tree, visitor)
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+#
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/common.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,333 @@
+from __future__ import generators
+from bike.globals import *
+from bike.parsing.fastparserast import getRoot, Function, Class, Module, getModule
+from bike.parsing.parserutils import generateLogicalLines, makeLineParseable, UnbalancedBracesException, generateLogicalLinesAndLineNumbers
+from bike.parsing.newstuff import getSourceNodesContainingRegex
+from bike.parsing import visitor
+from bike import log
+import compiler
+from compiler.ast import Getattr, Name
+import re
+
+class Match:
+ def __repr__(self):
+ return ",".join([self.filename, str(self.lineno), str(self.colno),
+ str(self.confidence)])
+ def __eq__(self,other):
+ if self is None or other is None:
+ return False
+ return self.filename == other.filename and \
+ self.lineno == other.lineno and \
+ self.colno == other.colno
+
+def getScopeForLine(sourceNode, lineno):
+ scope = None
+ childnodes = sourceNode.getFlattenedListOfFastParserASTNodes()
+ if childnodes == []:
+ return sourceNode.fastparseroot #module node
+
+ scope = sourceNode.fastparseroot
+
+ for node in childnodes:
+ if node.linenum > lineno: break
+ scope = node
+
+ if scope.getStartLine() != scope.getEndLine(): # is inline
+ while scope.getEndLine() <= lineno:
+ scope = scope.getParent()
+ return scope
+
+
+
+# global from the perspective of 'contextFilename'
+def globalScanForMatches(contextFilename, matchFinder, targetname):
+ for sourcenode in getSourceNodesContainingRegex(targetname, contextFilename):
+ print >> log.progress, "Scanning", sourcenode.filename
+ searchscope = sourcenode.fastparseroot
+ for match in scanScopeForMatches(sourcenode,searchscope,
+ matchFinder,targetname):
+ yield match
+
+
+def scanScopeForMatches(sourcenode,scope,matchFinder,targetname):
+ lineno = scope.getStartLine()
+ for line in generateLogicalLines(scope.getMaskedLines()):
+ if line.find(targetname) != -1:
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ scope = getScopeForLine(sourcenode, lineno)
+ matchFinder.reset(line)
+ matchFinder.setScope(scope)
+ matches = visitor.walk(ast, matchFinder).getMatches()
+ for index, confidence in matches:
+ match = Match()
+ match.filename = sourcenode.filename
+ match.sourcenode = sourcenode
+ x, y = indexToCoordinates(line, index)
+ match.lineno = lineno+y
+ match.colno = x
+ match.colend = match.colno+len(targetname)
+ match.confidence = confidence
+ yield match
+ lineno+=line.count("\n")
+
+
+def walkLinesContainingStrings(scope,astWalker,targetnames):
+ lineno = scope.getStartLine()
+ for line in generateLogicalLines(scope.getMaskedLines()):
+ if lineContainsOneOf(line,targetnames):
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ astWalker.lineno = lineno
+ matches = visitor.walk(ast, astWalker)
+ lineno+=line.count("\n")
+
+
+def lineContainsOneOf(line,targetnames):
+ for name in targetnames:
+ if line.find(name) != -1:
+ return True
+ return False
+
+
+# translates an idx in a logical line into physical line coordinates
+# returns x and y coords
+def indexToCoordinates(src, index):
+ y = src[: index].count("\n")
+ startOfLineIdx = src.rfind("\n", 0, index)+1
+ x = index-startOfLineIdx
+ return x, y
+
+
+
+# interface for MatchFinder classes
+# implement the visit methods
+class MatchFinder:
+ def setScope(self, scope):
+ self.scope = scope
+
+ def reset(self, line):
+ self.matches = []
+ self.words = re.split("(\w+)", line) # every other one is a non word
+ self.positions = []
+ i = 0
+ for word in self.words:
+ self.positions.append(i)
+ #if '\n' in word: # handle newlines
+ # i = len(word[word.index('\n')+1:])
+ #else:
+ i+=len(word)
+ self.index = 0
+
+ def getMatches(self):
+ return self.matches
+
+ # need to visit childnodes in same order as they appear
+ def visitPrintnl(self,node):
+ if node.dest:
+ self.visit(node.dest)
+ for n in node.nodes:
+ self.visit(n)
+
+ def visitName(self, node):
+ self.popWordsUpTo(node.name)
+
+ def visitClass(self, node):
+ self.popWordsUpTo(node.name)
+ for base in node.bases:
+ self.visit(base)
+
+ def zipArgs(self, argnames, defaults):
+ """Takes a list of argument names and (possibly a shorter) list of
+ default values and zips them into a list of pairs (argname, default).
+ Defaults are aligned so that the last len(defaults) arguments have
+ them, and the first len(argnames) - len(defaults) pairs have None as a
+ default.
+ """
+ fixed_args = len(argnames) - len(defaults)
+ defaults = [None] * fixed_args + list(defaults)
+ return zip(argnames, defaults)
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def visitGetattr(self,node):
+ self.visit(node.expr)
+ self.popWordsUpTo(node.attrname)
+
+ def visitAssName(self, node):
+ self.popWordsUpTo(node.name)
+
+ def visitAssAttr(self, node):
+ self.visit(node.expr)
+ self.popWordsUpTo(node.attrname)
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+ for name, alias in node.names:
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitLambda(self, node):
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def visitGlobal(self, node):
+ for name in node.names:
+ self.popWordsUpTo(name)
+
+ def popWordsUpTo(self, word):
+ if word == "*":
+ return # won't be able to find this
+ posInWords = self.words.index(word)
+ idx = self.positions[posInWords]
+ self.words = self.words[posInWords+1:]
+ self.positions = self.positions[posInWords+1:]
+
+ def appendMatch(self,name,confidence=100):
+ idx = self.getNextIndexOfWord(name)
+ self.matches.append((idx, confidence))
+
+ def getNextIndexOfWord(self,name):
+ return self.positions[self.words.index(name)]
+
+class CouldNotLocateNodeException(Exception): pass
+
+def translateSourceCoordsIntoASTNode(filename,lineno,col):
+ module = getModule(filename)
+ maskedlines = module.getMaskedModuleLines()
+ lline,backtrackchars = getLogicalLine(module, lineno)
+ doctoredline = makeLineParseable(lline)
+ ast = compiler.parse(doctoredline)
+ idx = backtrackchars+col
+ nodefinder = ASTNodeFinder(lline,idx)
+ node = compiler.walk(ast, nodefinder).node
+ if node is None:
+ raise CouldNotLocateNodeException("Could not translate editor coordinates into source node")
+ return node
+
+def getLogicalLine(module, lineno):
+ # we know that the scope is the start of a logical line, so
+ # we search from there
+ scope = getScopeForLine(module.getSourceNode(), lineno)
+ linegenerator = \
+ module.generateLinesWithLineNumbers(scope.getStartLine())
+ for lline,llinenum in \
+ generateLogicalLinesAndLineNumbers(linegenerator):
+ if llinenum > lineno:
+ break
+ prevline = lline
+ prevlinenum = llinenum
+
+ backtrackchars = 0
+ for i in range(prevlinenum,lineno):
+ backtrackchars += len(module.getSourceNode().getLines()[i-1])
+ return prevline, backtrackchars
+
+
+
+class ASTNodeFinder(MatchFinder):
+ # line is a masked line of text
+ # lineno and col are coords
+ def __init__(self,line,col):
+ self.line = line
+ self.col = col
+ self.reset(line)
+ self.node = None
+
+ def visitName(self,node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+
+ def visitGetattr(self,node):
+ self.visit(node.expr)
+ if self.checkIfNameMatchesColumn(node.attrname):
+ self.node = node
+ self.popWordsUpTo(node.attrname)
+
+ def visitFunction(self, node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if self.checkIfNameMatchesColumn(arg):
+ self.node = Name(arg)
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+
+ visitAssName = visitName
+ visitAssAttr = visitGetattr
+
+ def visitClass(self, node):
+ if self.checkIfNameMatchesColumn(node.name):
+ self.node = node
+ self.popWordsUpTo(node.name)
+ for base in node.bases:
+ self.visit(base)
+
+
+ def checkIfNameMatchesColumn(self,name):
+ idx = self.getNextIndexOfWord(name)
+ #print "name",name,"idx",idx,"self.col",self.col
+ if idx <= self.col and idx+len(name) > self.col:
+ return 1
+ return 0
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+ for name, alias in node.names:
+ if self.checkIfNameMatchesColumn(name):
+ self.node = self._manufactureASTNodeFromFQN(name)
+ return
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ # gets round the fact that imports etc dont contain nested getattr
+ # nodes for fqns (e.g. import a.b.bah) by converting the fqn
+ # string into a getattr instance
+ def _manufactureASTNodeFromFQN(self,fqn):
+ if "." in fqn:
+ assert 0, "getattr not supported yet"
+ else:
+ return Name(fqn)
+
+def isAMethod(scope,node):
+ return isinstance(node,compiler.ast.Function) and \
+ isinstance(scope,Class)
+
+def convertNodeToMatchObject(node,confidence=100):
+ m = Match()
+ m.sourcenode = node.module.getSourceNode()
+ m.filename = node.filename
+ if isinstance(node,Module):
+ m.lineno = 1
+ m.colno = 0
+ elif isinstance(node,Class) or isinstance(node,Function):
+ m.lineno = node.getStartLine()
+ m.colno = node.getColumnOfName()
+ m.confidence = confidence
+ return m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/findDefinition.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,277 @@
+from __future__ import generators
+from bike.query.common import Match, MatchFinder, \
+ getScopeForLine, indexToCoordinates, \
+ translateSourceCoordsIntoASTNode, scanScopeForMatches, \
+ isAMethod, convertNodeToMatchObject, walkLinesContainingStrings
+from bike.parsing.parserutils import generateLogicalLines,\
+ generateLogicalLinesAndLineNumbers, \
+ splitLogicalLines, makeLineParseable
+import compiler
+from compiler.ast import Getattr, Name, AssName, AssAttr
+from bike.parsing.fastparserast import getRoot, Package, Class, \
+ Module, Function, Instance
+import re
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType, \
+ isWordInLine, resolveImportedModuleOrPackage
+from bike.parsing import visitor
+from bike.parsing.visitor import walkAndGenerate
+
+from bike.parsing.parserutils import makeLineParseable,splitLogicalLines
+from bike.parsing.newstuff import getSourceNodesContainingRegex
+from bike.parsing.load import getSourceNode
+from bike import log
+
+
+class CantFindDefinitionException:
+ pass
+
+
+def findAllPossibleDefinitionsByCoords(filepath,lineno,col):
+
+ #try:
+ node = translateSourceCoordsIntoASTNode(filepath,lineno,col)
+ #except:
+ # import traceback
+ # traceback.print_exc()
+
+ if node is None:
+ raise "selected node type not supported"
+ scope = getScopeForLine(getSourceNode(filepath),lineno)
+ match = findDefinitionFromASTNode(scope,node)
+ if match is not None:
+ yield match
+ if isinstance(node,Getattr) and (match is None or match.confidence != 100):
+ root = getRoot()
+ name = node.attrname
+ for match in scanPythonPathForMatchingMethodNames(name,filepath):
+ yield match
+ print >>log.progress,"done"
+
+
+def findDefinitionFromASTNode(scope,node):
+ assert node is not None
+ if isinstance(node,Name) or isinstance(node,AssName):
+ while 1:
+ # try scope children
+ childscope = scope.getChild(node.name)
+ if childscope is not None:
+ return convertNodeToMatchObject(childscope,100)
+
+ if isinstance(scope,Package):
+ scope = scope.getChild("__init__")
+
+ # try arguments and assignments
+ match = scanScopeAST(scope,node.name,
+ AssignmentAndFnArgsSearcher(node.name))
+ if match is not None:
+ return match
+
+ # try imports
+ match = searchImportedModulesForDefinition(scope,node)
+ if match is not None:
+ return match
+
+
+ if not isinstance(scope,Module):
+ # try parent scope
+ scope = scope.getParent()
+ else:
+ break
+ assert isinstance(scope,Module)
+
+ elif isinstance(node,Getattr) or isinstance(node,AssAttr):
+ exprtype = getTypeOfExpr(scope,node.expr)
+ if not (exprtype is None or isinstance(exprtype,UnfoundType)):
+ if isinstance(exprtype,Instance):
+ exprtype = exprtype.getType()
+ match = findDefinitionOfAttributeFromASTNode(exprtype,
+ node.attrname)
+ else:
+ match = findDefinitionFromASTNode(exprtype,
+ Name(node.attrname))
+ if match is not None:
+ return match
+
+ elif isinstance(node,compiler.ast.Function) or \
+ isinstance(node,compiler.ast.Class):
+ if isAMethod(scope,node):
+ match = findDefinitionOfAttributeFromASTNode(scope,
+ node.name)
+ else:
+ match = findDefinitionFromASTNode(scope,Name(node.name))
+ if match is not None:
+ return match
+
+
+ type = getTypeOfExpr(scope,node)
+ if type is not None and (not isinstance(type,UnfoundType)) and \
+ (not isinstance(type,Instance)):
+ return convertNodeToMatchObject(type,100)
+ else:
+ return None
+
+
+def findDefinitionOfAttributeFromASTNode(type,name):
+ assert isinstance(type,Class)
+ attrfinder = AttrbuteDefnFinder([type],name)
+
+ # first scan the method names:
+ for child in type.getChildNodes():
+ if child.name == name:
+ return convertNodeToMatchObject(child,100)
+ # then scan the method source for attribues
+ for child in type.getChildNodes():
+ if isinstance(child,Function):
+ try:
+ return scanScopeForMatches(child.module.getSourceNode(),
+ child, attrfinder,
+ name).next()
+ except StopIteration:
+ continue
+
+
+class AttrbuteDefnFinder(MatchFinder):
+ def __init__(self,targetClasses,targetAttribute):
+ self.targetClasses = targetClasses
+ self.targetAttributeName = targetAttribute
+
+ def visitAssAttr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ if node.attrname == self.targetAttributeName:
+ exprtype = getTypeOfExpr(self.scope,node.expr)
+ if isinstance(exprtype,Instance) and \
+ exprtype.getType() in self.targetClasses:
+ self.appendMatch(self.targetAttributeName)
+ #else:
+ # self.appendMatch(self.targetAttributeName,50)
+ self.popWordsUpTo(node.attrname)
+
+
+
+
+def searchImportedModulesForDefinition(scope,node):
+ lines = scope.module.getSourceNode().getLines()
+ for lineno in scope.getImportLineNumbers():
+ logicalline = getLogicalLine(lines,lineno)
+ logicalline = makeLineParseable(logicalline)
+ ast = compiler.parse(logicalline)
+ class ImportVisitor:
+ def __init__(self,node):
+ self.target = node
+ self.match = None
+ assert isinstance(self.target,Name), \
+ "Getattr not supported"
+
+ def visitFrom(self, node):
+ module = resolveImportedModuleOrPackage(scope,node.modname)
+ if module is None: # couldn't find module
+ return
+
+ if node.names[0][0] == '*': # e.g. from foo import *
+ match = findDefinitionFromASTNode(module,self.target)
+ if match is not None:
+ self.match = match
+ return
+
+ for name, alias in node.names:
+ if alias is None and name == self.target.name:
+ match = findDefinitionFromASTNode(module,self.target)
+ if match is not None:
+ self.match = match
+ return
+
+
+ match = visitor.walk(ast, ImportVisitor(node)).match
+ if match:
+ return match
+ # loop
+
+
+def getLogicalLine(lines,lineno):
+ return generateLogicalLines(lines[lineno-1:]).next()
+
+class AssignmentAndFnArgsSearcher(MatchFinder):
+ def __init__(self,name):
+ self.targetname = name
+ self.match = None
+
+ def visitAssName(self, node):
+ if node.name == self.targetname:
+ idx = self.getNextIndexOfWord(self.targetname)
+ self.match = idx
+ return
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if arg == self.targetname:
+ idx = self.getNextIndexOfWord(self.targetname)
+ self.match = idx
+ return
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+ def getMatch(self):
+ return self.match
+
+
+
+# scans for lines containing keyword, and then runs the visitor over
+# the parsed AST for that line
+def scanScopeAST(scope,keyword,matchfinder):
+ lines = scope.generateLinesNotIncludingThoseBelongingToChildScopes()
+ match = None
+ for line,linenum in generateLogicalLinesAndLineNumbers(lines):
+ if isWordInLine(keyword, line):
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ matchfinder.reset(line)
+ match = visitor.walk(ast,matchfinder).getMatch()
+ if match is not None:
+ column,yoffset = indexToCoordinates(line,match)
+ m = createMatch(scope,linenum + yoffset,column)
+ return m
+ return None
+
+def createMatch(scope,lineno,x):
+ m = Match()
+ m.sourcenode = scope.module.getSourceNode()
+ m.filename = m.sourcenode.filename
+ m.lineno = lineno
+ m.colno = x
+ m.confidence = 100
+ return m
+
+# scan for methods globally (from perspective of 'perspectiveFilename')
+def scanPythonPathForMatchingMethodNames(name, contextFilename):
+ class MethodFinder:
+ def __init__(self,srcnode):
+ self.matches = []
+ self.srcnode = srcnode
+ def visitFunction(self,node):
+ node = getScopeForLine(self.srcnode, self.lineno)
+ if isinstance(node.getParent(),Class):
+ if node.name == name:
+ self.matches.append(convertNodeToMatchObject(node,50))
+
+ for srcnode in getSourceNodesContainingRegex(name,contextFilename):
+ m = MethodFinder(srcnode)
+ walkLinesContainingStrings(srcnode.fastparseroot,m,[name])
+ for match in m.matches:
+ yield match
+
+
+def getIndexOfWord(line,targetword):
+ words = re.split("(\w+)", line)
+ idx = 0
+ for word in words:
+ if word == targetword:
+ break
+ idx += len(word)
+ return idx
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/findReferences.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,224 @@
+from __future__ import generators
+from bike.globals import *
+from bike.parsing.fastparserast import Module, Class, Function, getRoot, Instance
+from bike.query.common import Match, MatchFinder,\
+ getScopeForLine, indexToCoordinates, \
+ translateSourceCoordsIntoASTNode, scanScopeForMatches,\
+ globalScanForMatches, isAMethod, convertNodeToMatchObject
+from compiler.ast import AssName,Name,Getattr,AssAttr
+import compiler
+from findDefinition import findDefinitionFromASTNode
+from bike.query.getTypeOf import getTypeOfExpr, UnfoundType
+from bike.query.relationships import getRootClassesOfHierarchy
+from bike import log
+from bike.parsing.load import getSourceNode
+
+
+class CouldntFindDefinitionException(Exception):
+ pass
+
+def findReferencesIncludingDefn(filename,lineno,col):
+ return findReferences(filename,lineno,col,1)
+
+
+def findReferences(filename,lineno,col,includeDefn=0):
+ sourcenode = getSourceNode(filename)
+ node = translateSourceCoordsIntoASTNode(filename,lineno,col)
+ assert node is not None
+ scope,defnmatch = getDefinitionAndScope(sourcenode,lineno,node)
+
+ try:
+ for match in findReferencesIncludingDefn_impl(sourcenode,node,
+ scope,defnmatch):
+ if not includeDefn and match == defnmatch:
+ continue # don't return definition
+ else:
+ yield match
+ except CouldntFindDefinitionException:
+ raise CouldntFindDefinitionException("Could not find definition. Please locate manually (maybe using find definition) and find references from that")
+
+def findReferencesIncludingDefn_impl(sourcenode,node,scope,defnmatch):
+ if isinstance(node,Name) or isinstance(node,AssName):
+ return generateRefsToName(node.name,scope,sourcenode,defnmatch)
+ elif isinstance(node,Getattr) or isinstance(node,AssAttr):
+ exprtype = getTypeOfExpr(scope,node.expr)
+ if exprtype is None or isinstance(exprtype,UnfoundType):
+ raise CouldntFindDefinitionException()
+
+ if isinstance(exprtype,Instance):
+ exprtype = exprtype.getType()
+ return generateRefsToAttribute(exprtype,node.attrname)
+
+ else:
+ targetname = node.attrname
+ return globalScanForMatches(sourcenode.filename,
+ NameRefFinder(targetname, defnmatch),
+ targetname, )
+ if match is not None:
+ return match
+ elif isinstance(node,compiler.ast.Function) or \
+ isinstance(node,compiler.ast.Class):
+ return handleClassOrFunctionRefs(scope, node, defnmatch)
+ else:
+ assert 0,"Seed to references must be Name,Getattr,Function or Class"
+
+def handleClassOrFunctionRefs(scope, node, defnmatch):
+ if isAMethod(scope,node):
+ for ref in generateRefsToAttribute(scope,node.name):
+ yield ref
+ else:
+ #yield convertNodeToMatchObject(node,100)
+ yield defnmatch
+ for ref in generateRefsToName(node.name,scope,
+ scope.module.getSourceNode(),
+ defnmatch):
+ yield ref
+
+def getDefinitionAndScope(sourcenode,lineno,node):
+ scope = getScopeForLine(sourcenode,lineno)
+ if scope.getStartLine() == lineno and \
+ scope.matchesCompilerNode(node): # scope is the node
+ return scope.getParent(), convertNodeToMatchObject(scope,100)
+ defnmatch = findDefinitionFromASTNode(scope,node)
+ if defnmatch is None:
+ raise CouldntFindDefinitionException()
+ scope = getScopeForLine(sourcenode,defnmatch.lineno)
+ return scope,defnmatch
+
+def generateRefsToName(name,scope,sourcenode,defnmatch):
+ assert scope is not None
+ if isinstance(scope,Function):
+ # search can be limited to scope
+ return scanScopeForMatches(sourcenode,scope,
+ NameRefFinder(name,defnmatch),
+ name)
+ else:
+ return globalScanForMatches(sourcenode.filename,
+ NameRefFinder(name,defnmatch),
+ name)
+
+
+class NameRefFinder(MatchFinder):
+ def __init__(self, targetstr,targetMatch):
+ self.targetstr = targetstr
+ self.targetMatch = targetMatch
+
+ def visitName(self, node):
+ if node.name == self.targetstr:
+ potentualMatch = findDefinitionFromASTNode(self.scope, node)
+ if potentualMatch is not None and \
+ potentualMatch == self.targetMatch:
+ self.appendMatch(node.name)
+ self.popWordsUpTo(node.name)
+
+ visitAssName = visitName
+
+ def visitFunction(self, node):
+ self.popWordsUpTo(node.name)
+ for arg, default in self.zipArgs(node.argnames, node.defaults):
+ if arg == self.targetstr:
+ self.appendMatch(arg)
+ self.popWordsUpTo(arg)
+ if default is not None:
+ self.visit(default)
+ self.visit(node.code)
+
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ self.popWordsUpTo(elem)
+
+ for name, alias in node.names:
+ if name == self.targetstr:
+ if alias is not None:
+ pretendNode = Name(alias)
+ else:
+ pretendNode = Name(name)
+ if findDefinitionFromASTNode(self.scope, pretendNode) \
+ == self.targetMatch:
+ self.appendMatch(name)
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+ if node.attrname == self.targetstr:
+ defn = findDefinitionFromASTNode(self.scope, node)
+ if defn is not None and defn == self.targetMatch:
+ self.appendMatch(node.attrname)
+ self.popWordsUpTo(node.attrname)
+
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ if name.split(".")[-1] == self.targetstr:
+ getattr = self.createGetattr(name)
+ if findDefinitionFromASTNode(self.scope, getattr) == self.targetMatch:
+ self.appendMatch(self.targetstr)
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+
+ def createGetattr(self,fqn):
+ node = Name(fqn[0])
+ for name in fqn.split(".")[1:]:
+ node = Getattr(node,name)
+ return node
+
+def generateRefsToAttribute(classobj,attrname):
+ rootClasses = getRootClassesOfHierarchy(classobj)
+ attrRefFinder = AttrbuteRefFinder(rootClasses,attrname)
+ for ref in globalScanForMatches(classobj.filename, attrRefFinder, attrname):
+ yield ref
+ print >>log.progress,"Done"
+
+
+class AttrbuteRefFinder(MatchFinder):
+ def __init__(self,rootClasses,targetAttribute):
+ self.rootClasses = rootClasses
+ self.targetAttributeName = targetAttribute
+
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ if node.attrname == self.targetAttributeName:
+ exprtype = getTypeOfExpr(self.scope,node.expr)
+
+ if isinstance(exprtype,Instance) and \
+ self._isAClassInTheSameHierarchy(exprtype.getType()):
+ self.appendMatch(self.targetAttributeName)
+ elif isinstance(exprtype,UnfoundType) or \
+ exprtype is None: # couldn't find type, so not sure
+ self.appendMatch(self.targetAttributeName,50)
+ else:
+ pass # definately not a match
+ self.popWordsUpTo(node.attrname)
+
+ visitAssAttr = visitGetattr
+
+ def visitFunction(self,node): # visit methods
+ if node.name == self.targetAttributeName:
+ parentScope = self.scope.getParent()
+ #print parentScope
+ #print self.targetClasses
+ if isinstance(parentScope,Class) and \
+ self._isAClassInTheSameHierarchy(parentScope):
+ self.appendMatch(node.name)
+
+ for c in node.getChildNodes():
+ self.visit(c)
+
+ def _isAClassInTheSameHierarchy(self,classobj):
+ #return classobj in self.targetClasses
+ targetRootClasses = getRootClassesOfHierarchy(classobj)
+ for rootclass in self.rootClasses:
+ if rootclass in targetRootClasses:
+ return True
+ return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getAllRelatedClasses.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,47 @@
+
+
+"""
+
+def getAllRelatedClasses(root,classfqn):
+ classobj = getTypeOf(root,classfqn)
+ rootClasses = _getRootClasses(classobj)
+ #print rootClasses
+ relatedClasses = [] + rootClasses
+ for rootClass in rootClasses:
+ relatedClasses += _getAllSubClasses(rootClass,root)
+ return relatedClasses
+
+def _getRootClasses(klass):
+ if klass is None: # i.e. dont have base class in our ast
+ return None
+ if klass.getBaseClassNames() == []: # i.e. is a root class
+ return[klass]
+ else:
+ rootclasses = []
+ for base in klass.getBaseClassNames():
+ baseclass = getTypeOf(klass,base)
+ rootclass = _getRootClasses(baseclass)
+ if rootclass is None: # base class not in our ast
+ rootclass = [klass]
+ rootclasses+=rootclass
+ return rootclasses
+
+
+def _getAllSubClasses(baseclass, root, subclasses = []):
+ class ClassVisitor:
+ def visitSource(self,node):
+ self.visit(node.fastparseroot)
+
+ def visitClass(self, node):
+ for basename in node.getBaseClassNames():
+ if basename.find(baseclass.name) != -1 and \
+ getTypeOf(node,basename) == baseclass:
+ subclasses.append(node)
+ _getAllSubClasses(node,root,subclasses)
+ for child in node.getChildNodes():
+ self.visit(child)
+
+ walk(root, ClassVisitor())
+ return subclasses
+
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getPackageDependencies.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+# fileInPackage is the filename of a file in the package hierarchy
+def getPackageDependencies(fileInPackage):
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getReferencesToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,62 @@
+from __future__ import generators
+from bike.query.common import Match, globalScanForMatches, getScopeForLine, MatchFinder
+from getTypeOf import getTypeOf, getTypeOfExpr
+import compiler
+import re
+
+def getReferencesToModule(root, fqn):
+
+ modulename = fqn.split(".")[-1]
+ moduleobj = getTypeOf(root, fqn)
+ moduleRefFinder = ModuleRefFinder(moduleobj)
+
+ for ref in globalScanForMatches(moduleRefFinder, modulename):
+ yield ref
+
+
+class ModuleRefFinder(MatchFinder):
+ def __init__(self, targetmodule):
+ self.targetmodule = targetmodule
+
+ def visitName(self, node):
+ if node.name == self.targetmodule.name:
+ if getTypeOfExpr(self.scope, node) == self.targetmodule:
+ self.appendMatch(node.name)
+ self.popWordsUpTo(node.name)
+
+ def visitImport(self, node):
+ for name, alias in node.names:
+ if name.split(".")[-1] == self.targetmodule.name:
+ if getTypeOf(self.scope, name) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ for nameelem in name.split("."):
+ self.popWordsUpTo(nameelem)
+ if alias is not None:
+ self.popWordsUpTo(alias)
+
+ def visitGetattr(self, node):
+ for c in node.getChildNodes():
+ self.visit(c)
+ if node.attrname == self.targetmodule.name:
+ if getTypeOfExpr(self.scope, node) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ self.popWordsUpTo(node.attrname)
+
+ def visitFrom(self, node):
+ for elem in node.modname.split("."):
+ if elem == self.targetmodule.name:
+ getTypeOf(self.scope, elem) == self.targetmodule
+ self.appendMatch(self.targetmodule.name)
+ self.popWordsUpTo(elem)
+
+ for name, alias in node.names:
+ if name == self.targetmodule.name:
+ if alias and \
+ getTypeOf(self.scope, alias) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ elif getTypeOf(self.scope, name) == self.targetmodule:
+ self.appendMatch(self.targetmodule.name)
+ if name != "*":
+ self.popWordsUpTo(name)
+ if alias is not None:
+ self.popWordsUpTo(alias)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/getTypeOf.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,377 @@
+# getTypeOf(scope,fqn) and getTypeOfExpr(scope,ast)
+
+from bike.parsing.fastparserast import Class, Function, Module, Root, getRoot, Package, Instance, getModule
+from bike.parsing.parserutils import generateLogicalLines, makeLineParseable,splitLogicalLines, makeLineParseable
+from bike.parsing import visitor
+from bike import log
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from bike.parsing.pathutils import getPackageBaseDirectory
+from bike.parsing.load import Cache
+import os
+import re
+import compiler
+
+# used if an assignment exists, but cant find type
+# e.g. a = SomeFunctionNotLoaded()
+# (as opposed to 'None' if cant find an assignment)
+class UnfoundType: pass
+
+
+getTypeOfStack = []
+
+# name is the fqn of the reference, scope is the scope ast object from
+# which the question is being asked.
+# returns an fastparser-ast object representing the type
+# or None if type not found
+def getTypeOf(scope, fqn):
+ if isinstance(scope, Root):
+ assert False, "Can't use getTypeOf to resolve from Root. Use getModuleOrPackageUsingFQN instead"
+
+
+ #print "getTypeOf:"+fqn+" -- "+str(scope)
+ #print
+ #print str(getTypeOfStack)
+ #print
+ if (fqn,scope) in getTypeOfStack: # loop protection
+ return None
+
+ # this is crap!
+ hashcode = str(scope)+fqn
+
+ try:
+ getTypeOfStack.append((fqn,scope))
+
+ try:
+ type = Cache.instance.typecache[hashcode]
+ except KeyError:
+ type = getTypeOf_impl(scope, fqn)
+ Cache.instance.typecache[hashcode] = type
+ return type
+ finally:
+ del getTypeOfStack[-1]
+
+
+
+def getTypeOf_impl(scope, fqn):
+ #print "getTypeOf_impl",scope,fqn
+ if fqn == "None":
+ return None
+
+ if "."in fqn:
+ rcdr = ".".join(fqn.split(".")[:-1])
+ rcar = fqn.split(".")[-1]
+ newscope = getTypeOf(scope,rcdr)
+ if newscope is not None:
+ return getTypeOf(newscope, rcar)
+ else:
+ #print "couldnt find "+rcdr+" in "+str(scope)
+ pass
+
+ assert scope is not None
+ #assert not ("." in fqn)
+
+ if isinstance(scope,UnfoundType):
+ return UnfoundType()
+
+ if isinstance(scope, Package):
+ #assert 0,scope
+ return handlePackageScope(scope, fqn)
+ elif isinstance(scope,Instance):
+ return handleClassInstanceAttribute(scope, fqn)
+ else:
+ return handleModuleClassOrFunctionScope(scope,fqn)
+
+
+
+def handleModuleClassOrFunctionScope(scope,name):
+ if name == "self" and isinstance(scope,Function) and \
+ isinstance(scope.getParent(),Class):
+ return Instance(scope.getParent())
+
+ matches = [c for c in scope.getChildNodes()if c.name == name]
+ if matches != []:
+ return matches[0]
+
+ type = scanScopeSourceForType(scope, name)
+ if type != None:
+ return type
+
+ #print "name = ",name,"scope = ",scope
+ type = getImportedType(scope, name) # try imported types
+ #print "type=",type
+ if type != None:
+ return type
+ parentScope = scope.getParent()
+ while isinstance(parentScope,Class):
+ # don't search class scope, since this is not accessible except
+ # through self (is this true?)
+ parentScope = parentScope.getParent()
+
+ if not (isinstance(parentScope,Package) or isinstance(parentScope,Root)):
+ return getTypeOf(parentScope, name)
+
+
+def handleClassInstanceAttribute(instance, attrname):
+ theClass = instance.getType()
+
+ # search methods and inner classes
+ match = theClass.getChild(attrname)
+ if match:
+ return match
+
+ #search methods for assignments with self.foo getattrs
+ for child in theClass.getChildNodes():
+ if not isinstance(child,Function):
+ continue
+ res = scanScopeAST(child,attrname,
+ SelfAttributeAssignmentVisitor(child,attrname))
+ if res is not None:
+ return res
+
+def handlePackageScope(package, fqn):
+ #print "handlePackageScope",package,fqn
+ child = package.getChild(fqn)
+ if child:
+ return child
+
+ if isinstance(package,Root):
+ return getModuleOrPackageUsingFQN(fqn)
+
+ # try searching the fs
+ node = getModuleOrPackageUsingFQN(fqn,package.path)
+ if node:
+ return node
+
+
+
+
+ # try the package init module
+ initmod = package.getChild("__init__")
+ if initmod is not None:
+ type = getImportedType(initmod, fqn)
+ if type:
+ return type
+ # maybe fqn is absolute
+ return getTypeOf(getRoot(), fqn)
+
+
+wordRE = re.compile("\w+")
+def isWordInLine(word, line):
+ if line.find(word) != -1:
+ words = wordRE.findall(line)
+ if word in words:
+ return 1
+ return 0
+
+def getImportedType(scope, fqn):
+ lines = scope.module.getSourceNode().getLines()
+ for lineno in scope.getImportLineNumbers():
+ logicalline = generateLogicalLines(lines[lineno-1:]).next()
+ logicalline = makeLineParseable(logicalline)
+ ast = compiler.parse(logicalline)
+ match = visitor.walk(ast, ImportVisitor(scope,fqn)).match
+ if match:
+ return match
+ #else loop
+
+class ImportVisitor:
+ def __init__(self,scope,fqn):
+ self.match = None
+ self.targetfqn = fqn
+ self.scope = scope
+
+ def visitImport(self, node):
+ # if target fqn is an import, then it must be a module or package
+ for name, alias in node.names:
+ if name == self.targetfqn:
+ self.match = resolveImportedModuleOrPackage(self.scope,name)
+ elif alias is not None and alias == self.targetfqn:
+ self.match = resolveImportedModuleOrPackage(self.scope,name)
+
+ def visitFrom(self, node):
+ if node.names[0][0] == '*': # e.g. from foo import *
+ if not "."in self.targetfqn:
+ module = resolveImportedModuleOrPackage(self.scope,
+ node.modname)
+ if module:
+ self.match = getTypeOf(module, self.targetfqn)
+ else:
+ for name, alias in node.names:
+ if alias == self.targetfqn or \
+ (alias is None and name == self.targetfqn):
+ scope = resolveImportedModuleOrPackage(self.scope,
+ node.modname)
+ if scope is not None:
+ if isinstance(scope,Package):
+ self.match = getModuleOrPackageUsingFQN(name,scope.path)
+ else:
+ assert isinstance(scope,Module)
+ self.match = getTypeOf(scope, name)
+
+
+
+
+class TypeNotSupportedException:
+ def __init__(self,msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+# attempts to evaluate the type of the expression
+def getTypeOfExpr(scope, ast):
+ if isinstance(ast, compiler.ast.Name):
+ return getTypeOf(scope, ast.name)
+
+ elif isinstance(ast, compiler.ast.Getattr) or \
+ isinstance(ast, compiler.ast.AssAttr):
+
+ # need to do this in order to match foo.bah.baz as
+ # a string in import statements
+ fqn = attemptToConvertGetattrToFqn(ast)
+ if fqn is not None:
+ return getTypeOf(scope,fqn)
+
+ expr = getTypeOfExpr(scope, ast.expr)
+ if expr is not None:
+ attrnametype = getTypeOf(expr, ast.attrname)
+ return attrnametype
+ return None
+
+ elif isinstance(ast, compiler.ast.CallFunc):
+ node = getTypeOfExpr(scope,ast.node)
+ if isinstance(node,Class):
+ return Instance(node)
+ elif isinstance(node,Function):
+ return getReturnTypeOfFunction(node)
+ else:
+ #raise TypeNotSupportedException, \
+ # "Evaluation of "+str(ast)+" not supported. scope="+str(scope)
+ print >> log.warning, "Evaluation of "+str(ast)+" not supported. scope="+str(scope)
+ return None
+
+
+def attemptToConvertGetattrToFqn(ast):
+ fqn = ast.attrname
+ ast = ast.expr
+ while isinstance(ast,compiler.ast.Getattr):
+ fqn = ast.attrname + "." + fqn
+ ast = ast.expr
+ if isinstance(ast,compiler.ast.Name):
+ return ast.name + "." + fqn
+ else:
+ return None
+
+
+getReturnTypeOfFunction_stack = []
+def getReturnTypeOfFunction(function):
+ if function in getReturnTypeOfFunction_stack: # loop protection
+ return None
+ try:
+ getReturnTypeOfFunction_stack.append(function)
+ return getReturnTypeOfFunction_impl(function)
+ finally:
+ del getReturnTypeOfFunction_stack[-1]
+
+def getReturnTypeOfFunction_impl(function):
+ return scanScopeAST(function,"return",ReturnTypeVisitor(function))
+
+
+# does parse of scope sourcecode to deduce type
+def scanScopeSourceForType(scope, name):
+ return scanScopeAST(scope,name,AssignmentVisitor(scope,name))
+
+
+# scans for lines containing keyword, and then runs the visitor over
+# the parsed AST for that line
+def scanScopeAST(scope,keyword,astvisitor):
+ lines = scope.getLinesNotIncludingThoseBelongingToChildScopes()
+ src = ''.join(lines)
+ match = None
+ #print "scanScopeAST:"+str(scope)
+ for line in splitLogicalLines(src):
+ if isWordInLine(keyword, line):
+ #print "scanning for "+keyword+" in line:"+line[:-1]
+ doctoredline = makeLineParseable(line)
+ ast = compiler.parse(doctoredline)
+ match = visitor.walk(ast,astvisitor).getMatch()
+ if match:
+ return match
+ return match
+
+
+class AssignmentVisitor:
+ def __init__(self,scope,targetName):
+ self.match=None
+ self.scope = scope
+ self.targetName = targetName
+
+ def getMatch(self):
+ return self.match
+
+ def visitAssign(self,node):
+ if isinstance(node.expr,compiler.ast.CallFunc):
+ for assnode in node.nodes:
+ if isinstance(assnode,compiler.ast.AssName) and \
+ assnode.name == self.targetName:
+ self.match = getTypeOfExpr(self.scope,node.expr)
+ if self.match is None:
+ self.match = UnfoundType()
+
+
+
+class SelfAttributeAssignmentVisitor:
+ def __init__(self,scope,targetName):
+ self.match=None
+ self.scope = scope
+ self.targetName = targetName
+
+ def getMatch(self):
+ return self.match
+
+ def visitAssign(self,node):
+ if isinstance(node.expr,compiler.ast.CallFunc):
+ for assnode in node.nodes:
+ if isinstance(assnode,compiler.ast.AssAttr) and \
+ isinstance(assnode.expr,compiler.ast.Name) and \
+ assnode.expr.name == "self" and \
+ assnode.attrname == self.targetName:
+ self.match = getTypeOfExpr(self.scope,node.expr)
+ #print "here!",self.match.getType().fqn
+
+
+class ReturnTypeVisitor:
+ def __init__(self,fn):
+ self.match=None
+ self.fn = fn
+
+ def getMatch(self):
+ return self.match
+
+ def visitReturn(self,node):
+ try:
+ self.match = getTypeOfExpr(self.fn,node.value)
+ except TypeNotSupportedException, ex:
+ pass
+
+
+def resolveImportedModuleOrPackage(scope,fqn):
+ # try searching from directory containing scope module
+ path = os.path.dirname(scope.module.filename)
+ node = getModuleOrPackageUsingFQN(fqn,path)
+ if node is not None:
+ return node
+
+ # try searching in same package hierarchy
+ basedir = getPackageBaseDirectory(scope.module.filename)
+ if fqn.split('.')[0] == os.path.split(basedir)[-1]:
+ # base package in fqn matches base directory
+ restOfFqn = ".".join(fqn.split('.')[1:])
+ node = getModuleOrPackageUsingFQN(restOfFqn,basedir)
+ if node is not None:
+ return node
+
+ # try searching the python path
+ node = getModuleOrPackageUsingFQN(fqn)
+ if node is not None:
+ return node
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/relationships.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,26 @@
+# queries to do with module/class/function relationships
+from __future__ import generators
+from bike.globals import *
+from getTypeOf import getTypeOf, getTypeOfExpr
+from bike.parsing.newstuff import generateModuleFilenamesInPythonPath, generateModuleFilenamesInPackage, getPythonPath
+from bike.parsing.pathutils import getPackageBaseDirectory
+from bike.query.common import MatchFinder, walkLinesContainingStrings, getScopeForLine
+from bike import log
+from bike.parsing.fastparserast import Module
+import re
+
+def getRootClassesOfHierarchy(klass):
+ if klass is None: # i.e. dont have base class in our ast
+ return None
+ if klass.getBaseClassNames() == []: # i.e. is a root class
+ return [klass]
+ else:
+ rootclasses = []
+ for base in klass.getBaseClassNames():
+ baseclass = getTypeOf(klass,base)
+ rootclass = getRootClassesOfHierarchy(baseclass)
+ if rootclass is None: # base class not in our ast
+ rootclass = [klass]
+ rootclasses+=rootclass
+ return rootclasses
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath(".."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_common.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+import compiler
+from bike.testutils import *
+from bike.parsing.load import getSourceNode
+from bike.parsing.fastparser import fastparser
+from bike.parsing.fastparserast import Module, Class
+from common import indexToCoordinates, getScopeForLine, walkLinesContainingStrings, translateSourceCoordsIntoASTNode
+
+
+class TestGetScopeForLine(BRMTestCase):
+
+ def test_worksWithFunctionScope(self):
+ src = trimLines("""
+ class a:
+ def foo():
+ pass
+ """)
+ node = createAST(src)
+ self.assertEqual(getScopeForLine(node,3).name,"foo")
+
+ def test_worksWithModuleScope(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ node = createAST(src)
+ assert isinstance(getScopeForLine(node,3),Module)
+
+ def test_worksWithInlineClass(self):
+ src = trimLines("""
+ class TheClass: pass""")
+ node = createAST(src)
+ assert isinstance(getScopeForLine(node,1),Class)
+
+
+class TestIndexToCoordinates(BRMTestCase):
+
+ def test_worksOnSingleLineString(self):
+ src = trimLines('''
+ foo bah
+ ''')
+ x,y = indexToCoordinates(src,src.index("bah"))
+ self.assertEqual(x,4)
+ self.assertEqual(y,0)
+ x,y = indexToCoordinates(src,src.index("foo"))
+ self.assertEqual(x,0)
+ self.assertEqual(y,0)
+
+ def test_worksOnMultilLineString(self):
+ src = trimLines('''
+ foo bah
+ baz boh
+ ''')
+ x,y = indexToCoordinates(src,src.index("boh"))
+ self.assertEqual(x,4)
+ self.assertEqual(y,1)
+
+
+class TestTranslateSourceCoordsIntoASTNode(BRMTestCase):
+
+ def test_worksOnImport(self):
+ src = trimLines('''
+ from foo import bar, baz
+ ''')
+ createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ node = translateSourceCoordsIntoASTNode(filename, 1, 17)
+ assert node.name == 'bar'
+
+ def test_worksOnMultiline(self):
+ src = trimLines("""
+ def foo(x,
+ y,
+ z):
+ return x*y*z
+ """)
+ createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ node = translateSourceCoordsIntoASTNode(filename, 3, 8)
+ assert node.name == 'z'
+
+
+
+
+class TestMatchFinder(BRMTestCase):
+
+ def test_visitLambda(self):
+ from common import MatchFinder
+ finder = MatchFinder()
+ src = '''x = lambda a, b, c=None, d=None: (a + b) and c or d'''
+ ast = compiler.parse(src)
+ finder.reset(src)
+ compiler.walk(ast, finder)
+
+
+class TestWalkLinesContainingStrings(BRMTestCase):
+ def test_walksClasses(self):
+ src=trimLines("""
+ class TestClass(a,
+ baseclass):
+ pass
+ """)
+ class MyWalker:
+ def visitClass(self, node):
+ self.basenames = []
+ for name in node.bases:
+ self.basenames.append(name.name)
+
+ writeTmpTestFile(src)
+ srcnode = getSourceNode(tmpfile)
+ walker = MyWalker()
+ walkLinesContainingStrings(srcnode.fastparseroot,walker,
+ "baseclass")
+ self.assertEqual(["a","baseclass"],walker.basenames)
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_findDefinition.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,632 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+
+from bike import testdata
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.query.getTypeOf import getTypeOf,resolveImportedModuleOrPackage
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from bike.parsing.fastparserast import getRoot
+from bike.testutils import *
+
+class TestFindDefinitionByCoords(BRMTestCase):
+
+ def test_findsClassRef(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,6)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def tests_findsMethodRef(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),5,3)]
+
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+
+ def test_returnsOtherMethodsWithSameName(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = SomeOtherClass()
+ a.theMethod()
+ """)
+
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),5,3)]
+
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 50
+
+
+
+
+ def test_findsTemporaryDefinition(self):
+ src=trimLines("""
+ a = 3
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),2,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsArgumentDefinition(self):
+ src=trimLines("""
+ def someFunction(a):
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),2,8)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 17
+ assert defn[0].confidence == 100
+
+ def test_findsClassInstanceDefinition(self):
+ src=trimLines("""
+ class TheClass():
+ pass
+ a = TheClass()
+ print a
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,6)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionInParentScope(self):
+ src=trimLines("""
+ a = 3
+ def foo(self):
+ b = a + 1
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,8)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionWithinFunction(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = someFunction()
+ print a
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),3,10)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionFromSubsequentAssignment(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = 3
+ print a
+ a = 5
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitionFromDefinition(self):
+ src=trimLines("""
+ def foo(yadda):
+ a = 3
+ print a
+ a = 5
+ """)
+ createSourceNodeAt(src,"mymodule")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(os.path.abspath("mymodule.py"),4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 4
+ assert defn[0].confidence == 100
+
+
+ def test_findsClassRefUsingFromImportStatement(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(classsrc, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,1,21)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingImportStatement(self):
+ importsrc=trimLines("""
+ import a.b.bah
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,14)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStarStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import *
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ createSourceNodeAt(importsrc,"a.foo")
+ createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsVariableRefUsingFromPackageImportModuleStatement(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ print bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,10)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsImportedVariableRefInAFunctionArg(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,17)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefUsingFromImportStatementInFunction(self):
+ importsrc=trimLines("""
+ def foo:
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,3,10)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a","b","bah.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+ def test_findsVariableRefByImportingModule(self):
+ importsrc=trimLines("""
+ import a.b.bah
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ defn = self.helper(importsrc, src, 2, 14)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def test_findsVariableRefByImportingModuleWithFrom(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+
+ defn = self.helper(importsrc, src, 2, 17)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 0
+ assert defn[0].confidence == 100
+
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ #Root(None,None,[pkgstructureRootDir])
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return defn
+
+ def test_doesntfindVariableRefOfUnimportedModule(self):
+ importsrc=trimLines("""
+ # a.b.bah not imported
+ print a.b.bah.mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ root = createSourceNodeAt(importsrc,"a.b.foo")
+ root = createSourceNodeAt(src, "a.b.bah")
+ filename = os.path.abspath(os.path.join("a","b","foo.py"))
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,14)]
+ self.assertEqual(defn,[])
+
+
+
+ def test_findsSelfAttributeDefinition(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,5,18)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 12
+ assert defn[0].confidence == 100
+
+ def test_findsSelfAttributeDefinitionFromSamePlace(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,3,12)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 3
+ assert defn[0].colno == 12
+ assert defn[0].confidence == 100
+
+
+ def test_findsSelfAttributeDefinition(self):
+ src=trimLines("""
+ class MyClass:
+ def someOtherFn(self):
+ pass
+ def load(self, source):
+ # fastparser ast
+ self.fastparseroot = fastparser(source,self.modulename)
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,6,14)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 6
+ assert defn[0].colno == 13
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefnOfInnerClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,4,14)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 10
+ assert defn[0].confidence == 100
+
+ def test_findsDefnOfOuterClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,4,4)]
+ assert defn[0].filename == os.path.abspath("mymodule.py")
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsClassDeclaredIn__init__Module(self):
+ importsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ src=trimLines("""
+ from a import TheClass
+ c = TheClass()
+ """)
+
+
+
+ root = createSourceNodeAt(importsrc,"a.__init__")
+ root = createSourceNodeAt(src, "mymodule")
+ filename = os.path.abspath("mymodule.py")
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,2,6)]
+ assert defn[0].filename == os.path.abspath(os.path.join("a",
+ "__init__.py"))
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+class TestFindDefinitionUsingFiles(BRMTestCase):
+ def test_findsASimpleDefinitionUsingFiles(self):
+ src=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ writeTmpTestFile(src)
+ defn = [x for x in findAllPossibleDefinitionsByCoords(tmpfile,3,6)]
+ assert defn[0].filename == tmpfile
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionInAnotherModuleUsingFiles(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ defn = self.helper(src, classsrc, 1, 21)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+
+
+ def test_findsDefinitionInAnotherRelativeModuleUsingFiles(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ pass
+ """)
+ defn = self.helper(src, classsrc,1,21)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def test_findsMethodDefinitionInAnotherModuleUsingFiles(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ defn = self.helper(src, classsrc, 3, 2)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+ def test_findsDefinitonOfMethodWhenUseIsOnAMultiLine(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ i,j = (32,
+ a.theMethod()) # <--- find me!
+ something=somethingelse
+ """)
+ defn = self.helper(src, classsrc, 4, 9)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 2
+ assert defn[0].colno == 8
+ assert defn[0].confidence == 100
+
+
+ def test_findsDefinitionWhenUseIsOnAMultilineAndNextLineBalancesBrace(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ c = TheClass()
+ f1, f2 = (c.func1,
+ c.theMethod)
+ f1, f2 = (c.func1,
+ c.theMethod)
+ """)
+ defn = self.helper(src, classsrc, 4, 10)
+ self.assertEqual(pkgstructureFile2,defn[0].filename)
+ self.assertEqual(2,defn[0].lineno)
+ self.assertEqual(8,defn[0].colno)
+ self.assertEqual(100,defn[0].confidence)
+
+ def test_worksIfFindingDefnOfRefInSlashMultiline(self):
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ src=trimLines("""
+ from b.bah import TheClass
+ c = TheClass()
+ f1, f2 = c.func1 \\
+ ,c.theMethod
+ """)
+ defn = self.helper(src, classsrc, 4, 10)
+ self.assertEqual(pkgstructureFile2,defn[0].filename)
+ self.assertEqual(2,defn[0].lineno)
+ self.assertEqual(8,defn[0].colno)
+ self.assertEqual(100,defn[0].confidence)
+
+ def test_findsDefnInSameNonPackageDirectory(self):
+ try:
+ getRoot().pythonpath = [] # clear the python path
+ classsrc = trimLines("""
+ def testFunction():
+ print 'hello'
+ """)
+ src = trimLines("""
+ from baz import testFunction
+ """)
+ writeTmpTestFile(src)
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, classsrc)
+ refs = [x for x in findAllPossibleDefinitionsByCoords(tmpfile,1,16)]
+ assert refs[0].filename == newtmpfile
+ assert refs[0].lineno == 1
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+
+
+ def test_findsDefnInPackageSubDirectoryAndRootNotInPath(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ getRoot().pythonpath = [] # clear the python path
+ defn = self.helper(src, classsrc, 1, 18)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def test_findsDefnInSamePackageHierarchyAndRootNotInPath(self):
+ src=trimLines("""
+ from a.b.bah import TheClass
+ """)
+ classsrc=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ getRoot().pythonpath = [] # clear the python path
+ defn = self.helper(src, classsrc, 1, 20)
+ assert defn[0].filename == pkgstructureFile2
+ assert defn[0].lineno == 1
+ assert defn[0].colno == 6
+ assert defn[0].confidence == 100
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ #Root(None,None,[pkgstructureRootDir])
+ defn = [x for x in findAllPossibleDefinitionsByCoords(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return defn
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_findReferences.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,516 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+#from bike.testutils import trimLines, createSourceNodeAt, \
+# createSourceNodeAt_old, BRMTestCase
+from bike import testdata
+from findReferences import findReferences, findReferencesIncludingDefn
+from bike.query.getTypeOf import getTypeOf
+
+class helpers:
+ def helper(self,src,lineno,colno):
+ writeTmpTestFile(src)
+ refs = [x for x in findReferences(tmpfile,lineno,colno)]
+ return refs
+
+ def helper2(self,src,lineno,colno):
+ writeTmpTestFile(src)
+ refs = [x for x in findReferencesIncludingDefn(tmpfile,lineno,
+ colno)]
+ return refs
+
+ def helper3(self, src, importedsrc, line, col):
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile2
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ return refs
+
+ def helper4(self, src, importedsrc, line, col):
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile1
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ return refs
+
+
+
+class TestFindReferences(BRMTestCase,helpers):
+ def test_findsSimpleReferencesGivenAssignment(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ refs = self.helper(src,3,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+
+
+ def test_findsSimpleReferencesGivenReference(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+
+ refs = self.helper2(src,3,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 4
+ assert refs[0].confidence == 100
+
+
+ def test_findsReferencesToOtherAssignments(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ a = 4
+ """)
+ refs = self.helper(src,2,4)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 4
+ assert refs[0].confidence == 100
+
+ def test_findsFunctionArg(self):
+ src=trimLines("""
+ def foo(a):
+ print a
+ """)
+ refs = self.helper2(src,2,10)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 8
+ assert refs[0].confidence == 100
+
+ def test_findsFunctionArgWithDefault(self):
+ src=trimLines("""
+ def foo(a=None, b=None):
+ print a, b
+ """)
+ refs = self.helper(src,1,4)
+ self.assertEquals(refs, [])
+
+ def test_findsFunctionArgWithDefault2(self):
+ src=trimLines("""
+ def foo(a=None, b=None):
+ print a, b
+ """)
+ refs = self.helper2(src,2,13)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 16
+ assert refs[0].confidence == 100
+
+
+ def test_findsReferencesGivenFunctionArg(self):
+ src=trimLines("""
+ def foo(a):
+ print a
+ """)
+ refs = self.helper(src,1,8)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+
+ def test_findsVariableRefInImportStatementUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[0].lineno == 1
+ assert refs[0].colno == 20
+ assert refs[0].confidence == 100
+
+
+
+ def test_findsVariableRefUsingFromImportStatement(self):
+ importsrc=trimLines("""
+ from a.b.bah import mytext
+ print mytext
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[1].lineno == 2
+ assert refs[1].colno == 6
+ assert refs[1].confidence == 100
+
+ def test_findsImportedVariableRefInAFunctionArg(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ someFunction(bah.mytext)
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ refs = self.helper3(importsrc,src,1,1)
+ assert refs[0].filename == pkgstructureFile1
+ assert refs[0].lineno == 2
+ assert refs[0].colno == 17
+ assert refs[0].confidence == 100
+
+ def test_getsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ refs = self.helper4(src,testdata.TheClass,3,2)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+
+
+ def test_findsRefToSelfAttribute(self):
+ src=trimLines("""
+ class MyClass:
+ def __init__(self):
+ self.a = 'hello'
+ def myMethod(self):
+ print self.a
+ """)
+ refs = self.helper(src,3,12)
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 5
+ assert refs[0].colno == 18
+ assert refs[0].confidence == 100
+
+
+class FindReferencesToMethod(BRMTestCase,helpers):
+ def test_findsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallFromClassImportedWithAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceOfMethodCallWhenInstanceReturnedByFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ return TheClass()
+ a = foo()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,6)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallInSameClass(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+ refs = self.helper4(src,testClass,2,8)
+ assert refs[0].filename == pkgstructureFile1
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,13)
+
+ def test_getsReferenceOfMethodOnBaseClassInstance(self):
+ src = trimLines("""
+ class root:
+ def theMethod(self):
+ pass
+
+ class a(root):
+ def theMethod(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[2].filename,pkgstructureFile1)
+ self.assertEqual(refs[2].lineno,17)
+ self.assertEqual(refs[2].colno,13)
+
+ def test_doesntGetReferenceToMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ refs = self.helper3(src,testClass,2,8)
+ assert refs == []
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,10)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceInMiddleOfBiggerCompoundCall(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+
+ refs = self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,11)
+ self.assertEqual(refs[0].colend,20)
+
+ def test_doesntBarfWhenObjectIsArrayMember(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a[0] = TheClass()
+ a[0].theMethod()
+ """)
+ refs = self.helper4(src,"pass",2,8)
+ # should get to here without exception
+
+class FindReferencesToClass(BRMTestCase, helpers):
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ assert refs == []
+
+
+ def test_findsSimpleReferenceInSameModule(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_doesntBarfOnSingleLineSourceWithInlineClass(self):
+ src=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ """)
+ refs = self.helper3(src,"class TheClass: pass",1,6)
+ assert refs != []
+
+ def test_findsReferenceToClassImportedInSameClassScope(self):
+ src=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ refs = self.helper3(src,"class TheClass: pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,22)
+
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,4)
+
+ def testFindsClassReferenceWhenScopeIsSameNameAsClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ refs = self.helper4(src,"pass",2,10)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,13)
+ self.assertEqual(refs[0].confidence,100)
+
+ def testFindsClassReferenceWhenChildIsSameNameAsClass(self):
+ src = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ refs = self.helper4(src,"pass",1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+
+class TestFindReferencesIncludingDefn(BRMTestCase,helpers):
+ def test_findsMethodDecl(self):
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ refs = self.helper2(src,2,8)
+ self.assertEqual(refs[0].filename,tmpfile)
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,8)
+ self.assertEqual(refs[0].confidence,100)
+
+
+
+class TestFindReferencesUsingFiles(BRMTestCase):
+ def test_findsSimpleReferencesUsingFiles(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ refs = self.helper("pass",src,2,4)
+ assert refs[0].filename == pkgstructureFile2
+ assert refs[0].lineno == 3
+ assert refs[0].colno == 10
+ assert refs[0].confidence == 100
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromAndAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ """)
+ refs = self.helper(src,testClass,1,6)
+ self.assertEqual(refs[0].filename,pkgstructureFile1)
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_doesntBarfWhenCantLocatePackageWhenTryingToFindBaseClass(self):
+ src = trimLines("""
+ from doesntexist import baseclass
+ class foo(baseclass):
+ def myMethod(self):
+ pass
+ """)
+ refs = self.helper("",src,3,8)
+
+ def test_doesntBarfWhenComesAcrossAPrintNl(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+
+ print >>foo, TheClass
+ """)
+ refs = self.helper("",src,1,6)
+
+
+ def test_returnsOtherFilesInSameNonPackageDirectory(self):
+ try:
+ getRoot().pythonpath = [] # clear the python path
+ classsrc = trimLines("""
+ def testFunction():
+ print 'hello'
+ """)
+ src = trimLines("""
+ from baz import testFunction
+ """)
+ writeTmpTestFile(src)
+ newtmpfile = os.path.join(tmproot,"baz.py")
+ writeFile(newtmpfile, classsrc)
+ refs = [x for x in findReferences(newtmpfile,1,4)]
+
+ assert refs[0].filename == tmpfile
+ assert refs[0].lineno == 1
+ finally:
+ os.remove(newtmpfile)
+ deleteTmpTestFile()
+
+
+
+
+ def helper(self, src, classsrc, line, col):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile2
+ refs = [x for x in findReferences(filename,line,col)]
+ finally:
+ removePackageStructure()
+ return refs
+
+
+
+
+testClass = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getPackageDependencies.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+
+class TestGetPackageDependencies(BRMTestCase):
+ def test_foo(self):
+ assert 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToClass.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,280 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.findReferences import findReferences
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToClass(BRMTestCase):
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ refs = [x for x in findReferences(os.path.abspath("mymodule.py"),1,6)]
+ self.assertEqual(refs,[])
+
+ def test_findsSimpleReferenceInSameModule(self):
+ src = trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ refs = [x for x in findReferences(os.path.abspath("mymodule.py"),1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath("mymodule.py"))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferencesInModuleWhichImportsClass(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ refs = [x for x in findReferences(os.path.abspath("a/b/bah.py"),1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsReferenceInModuleWhichImportsClassWithFrom(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a/foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,8)
+ self.assertEqual(refs[1].confidence,100)
+
+ def test_findsReferenceToClassImportedInSameClassScope(self):
+ src=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ assert refs != []
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromAndAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsReferenceInModuleWhichImportsClassWithImportAs(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar(self):
+ src = trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar2(self):
+ src = trimLines("""
+ from a.b.bah import *
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsClassReferenceInInstanceCreation(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ root = createSourceNodeAt(src, "a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,0)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_findsClassReferenceInInstanceCreationWithFQN(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_doesntfindReferenceInModuleWhichDoesntImportClass(self):
+ src = trimLines("""
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ assert refs == []
+
+ def test_findsReferenceInClassBases(self):
+ src =trimLines("""
+ from b.bah import TheClass
+ class DerivedClass(TheClass):
+ pass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,2)
+ self.assertEqual(refs[1].colno,19)
+ self.assertEqual(refs[1].confidence,100)
+
+
+
+ def test_findsReferenceInMultiLineImportStatement(self):
+ src =trimLines("""
+ from b.bah import foo, \\
+ TheFooBah, TheClass, Foobah, SomethingElse
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,21)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceWhenModulenameSameAsClassMethodName(self):
+ # asserts that brm doesnt search class scope after not finding name
+ # in method scope (since class scope is invisible unless called on 'self'
+ src =trimLines("""
+ from a.b import bah
+ class baz:
+ def bah(self):
+ print bah.TheClass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,4)
+ self.assertEqual(refs[0].colno,18)
+ self.assertEqual(refs[0].confidence,100)
+
+
+ def test_doesntBarfOnFromImportStarWhenNameIsInFromClause(self):
+ src = trimLines("""
+ from a.b.bah import TheClass
+ a = TheClass()
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(ClassTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,1,6)]
+
+
+ClassTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.findReferences import findReferences
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToMethod(BRMTestCase):
+
+ def test_getsReferenceOfSimpleMethodCall(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,2)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallFromClassImportedWithAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceOfMethodCallWhenInstanceReturnedByFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ return TheClass()
+ a = foo()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,6)
+ self.assertEqual(refs[0].colno,2)
+
+ def test_getsReferenceOfMethodCallInSameClass(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,5)
+ self.assertEqual(refs[0].colno,13)
+
+ def test_getsReferenceOfMethodOnBaseClassInstance(self):
+ src = trimLines("""
+ class root:
+ def theMethod():
+ pass
+
+ class a(root):
+ def theMethod():
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+
+ rootinstance = root()
+ rootinstance.theMethod()
+ """)
+
+ refs =self.helper4(src,"pass",2,8)
+ self.assertEqual(refs[2].filename,pkgstructureFile1)
+ self.assertEqual(refs[2].lineno,17)
+ self.assertEqual(refs[2].colno,13)
+
+ def helper4(self, src, importedsrc, line, col):
+ try:
+ createPackageStructure(src,importedsrc)
+ filename = pkgstructureFile1
+ refs = [x for x in findReferences(filename,line,col)
+ if x.confidence == 100]
+ finally:
+ removePackageStructure()
+ return refs
+
+ def test_doesntGetReferenceToMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(MethodTestdata, "a.b.bah")
+ filename = os.path.abspath("a/b/bah.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ assert len(refs) == 0
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,10)
+ self.assertEqual(refs[0].colno,6)
+
+
+ def test_getsReferenceInMiddleOfBiggerCompoundCall(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+
+ root = createSourceNodeAt(src,"a.foo")
+ filename = os.path.abspath("a/foo.py")
+ refs = [x for x in findReferences(filename,2,8)
+ if x.confidence == 100]
+ self.assertEqual(refs[0].filename,
+ os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,3)
+ self.assertEqual(refs[0].colno,11)
+ self.assertEqual(refs[0].colend,20)
+
+
+MethodTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getReferencesToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getReferencesToModule import *
+from bike.parsing.fastparserast import Module
+
+class TestGetReferencesToModule(BRMTestCase):
+
+ def test_returnsEmptyListIfNoReferences(self):
+ src = trimLines("""
+ class MyClass:
+ pass
+ a = TheClass()
+ """)
+ root = createSourceNodeAt(src,"mymodule")
+ self.assertEqual([x for x in getReferencesToModule(root,"myothermodule")],[])
+
+ def test_findsReferencesInModuleWhichImportsModule(self):
+ src = trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,9)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,10)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFrom(self):
+ src = trimLines("""
+ from b import bah
+ def foo():
+ a = bah.TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a/foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,8)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFromAndAlias(self):
+ src = trimLines("""
+ from b import bah as mymodule
+ def foo():
+ a = mymodule.MyTheClass()
+ a.theMethod()
+ """)
+
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,14)
+ self.assertEqual(refs[0].confidence,100)
+
+ """ # mymodule.MyTheClass
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,3)
+ self.assertEqual(refs[1].colno,10)
+ self.assertEqual(refs[1].confidence,100)
+ """
+
+ def test_findsReferenceInModuleWhichImportsModuleWithFromImportStar(self):
+ src = trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,7)
+ self.assertEqual(refs[0].confidence,100)
+
+ ''' Dont think this is a valid test, since cant import a module with
+ from package import *
+ def test_findsReferenceInModuleWhichImportsClassWithFromImportStar2(self):
+ src = trimLines("""
+ from a.b import *
+ a = bah.TheClass()
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,4)
+ self.assertEqual(refs[0].confidence,100)
+ '''
+
+ def test_findsReferenceInClassBases(self):
+ src =trimLines("""
+ from b import bah
+ class DerivedClass(bah.TheClass):
+ pass
+ """)
+
+ root = createSourceNodeAt(src, "a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[1].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[1].lineno,2)
+ self.assertEqual(refs[1].colno,19)
+ self.assertEqual(refs[1].confidence,100)
+
+ def test_findsReferenceInMultiLineImportStatement(self):
+ src =trimLines("""
+ from b import foo, \\
+ TheFooBah, TheClass, TheBastard, SomethingElse, bah
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,2)
+ self.assertEqual(refs[0].colno,58)
+ self.assertEqual(refs[0].confidence,100)
+
+ def test_findsReferenceWhenModulenameSameAsClassMethodName(self):
+ # asserts that brm doesnt search class scope after not finding name
+ # in method scope (since class scope is invisible unless called on 'self'
+ src =trimLines("""
+ from a.b import bah
+ class baz:
+ def bah(self):
+ print bah.TheClass
+ """)
+
+ root = createSourceNodeAt( src, "a.foo")
+ root = createSourceNodeAt( testdata.TheClass, "a.b.bah")
+ refs = [x for x in getReferencesToModule(root,"a.b.bah")]
+
+ self.assertEqual(refs[0].filename,os.path.abspath(os.path.join("a","foo.py")))
+ self.assertEqual(refs[0].lineno,1)
+ self.assertEqual(refs[0].colno,16)
+ self.assertEqual(refs[0].confidence,100)
+
+ assert (len(refs))==2
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_getTypeOf.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getTypeOf import getTypeOf, UnfoundType,\
+ attemptToConvertGetattrToFqn
+from bike.parsing.fastparserast import Class, Function, Instance
+from bike.parsing.newstuff import getModuleOrPackageUsingFQN
+from compiler.ast import Getattr,CallFunc,Name
+
+class TestGetTypeOf(BRMTestCase):
+ def test_getsTypeOfSimpleClassInstanceReference(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfImportedClassReference(self):
+ src = trimLines("""
+ import b.bah
+ a = b.bah.TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfClassReferenceFromImportedPackage(self):
+ src = trimLines("""
+ import b
+ a = b.bah.TheClass()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ module = getModuleOrPackageUsingFQN("a.foo")
+ res = getTypeOf(module,"a")
+ assert isinstance(res,Instance)
+ assert isinstance(res.getType(),Class)
+ assert res.getType().name == "TheClass"
+
+ def test_getsTypeOfInstanceThatIsAnAttributeOfSelf(self):
+ src = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ module = getModuleOrPackageUsingFQN('a.foo')
+ theclass = getTypeOf(module,"TheClass")
+ fn = getTypeOf(module,"AnotherClass.anotherFn")
+ self.assertEqual(getTypeOf(fn,"self.a").getType().name, "TheClass")
+ #self.assertEqual(getTypeOf(fn,"self.a").getType(), theclass)
+
+
+
+ def test_doesntGetTypeDefinedInChildFunction(self):
+ src = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ assert isinstance(getTypeOf(themodule,"a"),UnfoundType)
+
+
+ def test_getsTypeOfClassReferencedViaAlias(self):
+ src = trimLines("""
+ from b.bah import TheClass as FooBah
+ FooBah()
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ root = createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ self.assertEqual(getTypeOf(themodule,"FooBah").name,"TheClass")
+ self.assertEqual(getTypeOf(themodule,"FooBah").filename,
+ os.path.abspath(os.path.join("a","b","bah.py")))
+
+
+ def test_getsTypeOfClassImportedFromPackageScope(self):
+ initfile = trimLines("""
+ from bah import TheClass
+ """)
+ src = trimLines("""
+ from a import b
+ b.TheClass()
+ """)
+ createSourceNodeAt(src,"a.foo")
+ createSourceNodeAt(testdata.TheClass, "a.b.bah")
+ createSourceNodeAt(initfile,"a.b.__init__")
+ themodule = getModuleOrPackageUsingFQN("a.foo")
+ self.assertEqual(getTypeOf(themodule,"b.TheClass").name,"TheClass")
+ self.assertEqual(getTypeOf(themodule,"b.TheClass").filename,
+ os.path.abspath(os.path.join("a","b","bah.py")))
+
+
+ def test_attemptToConvertGetattrToFqn_returnsNoneIfFails(self):
+ ast = Getattr(CallFunc(Name("foo"),[],[],[]),"hello")
+ assert attemptToConvertGetattrToFqn(ast) is None
+
+ def test_attemptToConvertGetattrToFqn_works(self):
+ ast = Getattr(Getattr(Name("foo"),"bah"),"hello")
+ assert attemptToConvertGetattrToFqn(ast) == "foo.bah.hello"
+
+
+ def test_handlesRecursionProblem(self):
+ src = trimLines("""
+ def fn(root):
+ node = root
+ node = node.getPackage('something')
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ m = getModuleOrPackageUsingFQN("a.foo")
+ fn = getTypeOf(m,"fn")
+ getTypeOf(fn,"node") # stack overflow!
+
+
+ def test_doesntGotIntoRecursiveLoopWhenEvaluatingARecursiveFunction(self):
+ src = trimLines("""
+ def fn(v):
+ if v < 45:
+ return fn(root+1)
+ val = fn(3)
+ """)
+ root = createSourceNodeAt(src,"a.foo")
+ mod = getModuleOrPackageUsingFQN("a.foo")
+ getTypeOf(mod,"val") # stack overflow!
+
+ def test_getsModuleImportedWithFrom(self):
+ importsrc=trimLines("""
+ from a.b import bah
+ """)
+ src=trimLines("""
+ mytext = 'hello'
+ """)
+ type = self.helper(importsrc,src,'bah')
+ self.assertEqual(pkgstructureFile2,type.filename)
+
+ def helper(self,importsrc,src,name):
+ try:
+ createPackageStructure(importsrc,src)
+ from bike.parsing.newstuff import getModule
+ scope = getModule(pkgstructureFile1)
+ return getTypeOf(scope,name)
+ finally:
+ removePackageStructure()
+
+
+ def test_getsTypeOfClassImportedAsAlias(self):
+ importsrc = trimLines("""
+ from b.bah import TheClass as MyTheClass
+
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ src=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ type = self.helper(importsrc,src,'MyTheClass')
+ self.assertEqual("TheClass",type.name)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/test_relationships.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+import os
+from bike import testdata
+from bike.testutils import *
+from bike.query.getTypeOf import getTypeOf
+from bike.parsing.fastparserast import Module
+from bike.query.relationships import getRootClassesOfHierarchy
+from bike.parsing.newstuff import getModule
+
+
+class TestGetRootClassesOfHierarchy(BRMTestCase):
+ def test_getsRootClassFromDerivedClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ """)
+ rootclasses = self.helper(src,"DerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual(len(rootclasses),1)
+
+ def test_getsRootClassFromDerivedDerivedClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual(len(rootclasses),1)
+
+
+ def test_getsRootClassFromDiamondOfClasses(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("TheClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+
+ def test_getsRootClassesFromMultipleInheritance(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass:
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("DerivedClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+ def test_getsRootClassesFromMultipleInheritanceWithNewStyleClass(self):
+ src = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(Object):
+ pass
+ class DerivedDerivedClass(DerivedClass,BaseClass):
+ pass
+ """)
+ rootclasses = self.helper(src,"DerivedDerivedClass")
+ self.assertEqual("DerivedClass",rootclasses[0].name)
+ self.assertEqual("TheClass",rootclasses[1].name)
+ self.assertEqual(len(rootclasses),2)
+
+
+ def helper(self,src,classname):
+ try:
+ createPackageStructure(src,testdata.TheClass)
+ classobj = getTypeOf(getModule(pkgstructureFile1),classname)
+ return getRootClassesOfHierarchy(classobj)
+ finally:
+ removePackageStructure()
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/query/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+import setpath
+
+from test_common import *
+from test_getReferencesToClass import *
+from test_getReferencesToMethod import *
+#from test_getReferencesToModule import *
+from test_findDefinition import *
+from test_findReferences import *
+from test_relationships import *
+from test_getTypeOf import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/__init__.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,1 @@
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/extractMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,363 @@
+import re
+import compiler
+from bike.parsing import visitor
+from bike.query.common import getScopeForLine
+from bike.parsing.parserutils import generateLogicalLines, \
+ makeLineParseable, maskStringsAndRemoveComments
+from parser import ParserError
+from bike.parsing.fastparserast import Class
+from bike.transformer.undo import getUndoStack
+from bike.refactor.utils import getTabWidthOfLine, getLineSeperator, \
+ reverseCoordsIfWrongWayRound
+from bike.transformer.save import queueFileToSave
+from bike.parsing.load import getSourceNode
+TABSIZE = 4
+
+class coords:
+ def __init__(self, line, column):
+ self.column = column
+ self.line = line
+ def __str__(self):
+ return "("+str(self.column)+","+str(self.line)+")"
+
+commentRE = re.compile(r"#.*?$")
+
+class ParserException(Exception): pass
+
+def extractMethod(filename, startcoords, endcoords, newname):
+ ExtractMethod(getSourceNode(filename),
+ startcoords, endcoords, newname).execute()
+
+class ExtractMethod(object):
+ def __init__(self,sourcenode, startcoords, endcoords, newname):
+ self.sourcenode = sourcenode
+
+ startcoords, endcoords = \
+ reverseCoordsIfWrongWayRound(startcoords,endcoords)
+
+ self.startline = startcoords.line
+ self.endline = endcoords.line
+ self.startcol = startcoords.column
+ self.endcol= endcoords.column
+
+ self.newfn = NewFunction(newname)
+
+ self.getLineSeperator()
+ self.adjustStartColumnIfLessThanTabwidth()
+ self.adjustEndColumnIfStartsANewLine()
+ self.fn = self.getFunctionObject()
+ self.getRegionToBuffer()
+ #print "-"*80
+ #print self.extractedLines
+ #print "-"*80
+ self.deduceIfIsMethodOrFunction()
+
+ def execute(self):
+ self.deduceArguments()
+ getUndoStack().addSource(self.sourcenode.filename,
+ self.sourcenode.getSource())
+ srclines = self.sourcenode.getLines()
+ newFnInsertPosition = self.fn.getEndLine()-1
+ self.insertNewFunctionIntoSrcLines(srclines, self.newfn,
+ newFnInsertPosition)
+ self.writeCallToNewFunction(srclines)
+
+ src = "".join(srclines)
+ queueFileToSave(self.sourcenode.filename,src)
+
+ def getLineSeperator(self):
+ line = self.sourcenode.getLines()[self.startline-1]
+ linesep = getLineSeperator(line)
+ self.linesep = linesep
+
+ def adjustStartColumnIfLessThanTabwidth(self):
+ tabwidth = getTabWidthOfLine(self.sourcenode.getLines()[self.startline-1])
+ if self.startcol < tabwidth: self.startcol = tabwidth
+
+ def adjustEndColumnIfStartsANewLine(self):
+ if self.endcol == 0:
+ self.endline -=1
+ nlSize = len(self.linesep)
+ self.endcol = len(self.sourcenode.getLines()[self.endline-1])-nlSize
+
+
+ def getFunctionObject(self):
+ return getScopeForLine(self.sourcenode,self.startline)
+
+
+ def getTabwidthOfParentFunction(self):
+ line = self.sourcenode.getLines()[self.fn.getStartLine()-1]
+ match = re.match("\s+",line)
+ if match is None:
+ return 0
+ else:
+ return match.end(0)
+
+ # should be in the transformer module
+ def insertNewFunctionIntoSrcLines(self,srclines,newfn,insertpos):
+ tabwidth = self.getTabwidthOfParentFunction()
+
+ while re.match("\s*"+self.linesep,srclines[insertpos-1]):
+ insertpos -= 1
+
+ srclines.insert(insertpos, self.linesep)
+ insertpos +=1
+
+ fndefn = "def "+newfn.name+"("
+
+ if self.isAMethod:
+ fndefn += "self"
+ if newfn.args != []:
+ fndefn += ", "+", ".join(newfn.args)
+ else:
+ fndefn += ", ".join(newfn.args)
+
+ fndefn += "):"+self.linesep
+
+
+ srclines.insert(insertpos,tabwidth*" "+fndefn)
+ insertpos +=1
+
+ tabwidth += TABSIZE
+
+
+ if self.extractedCodeIsAnExpression(srclines):
+ assert len(self.extractedLines) == 1
+
+ fnbody = [tabwidth*" "+ "return "+self.extractedLines[0]]
+
+
+ else:
+ fnbody = [tabwidth*" "+line for line in self.extractedLines]
+ if newfn.retvals != []:
+ fnbody.append(tabwidth*" "+"return "+
+ ", ".join(newfn.retvals) + self.linesep)
+
+ for line in fnbody:
+ srclines.insert(insertpos,line)
+ insertpos +=1
+
+
+ def writeCallToNewFunction(self, srclines):
+ startline = self.startline
+ endline = self.endline
+ startcol = self.startcol
+ endcol= self.endcol
+
+ fncall = self.constructFunctionCallString(self.newfn.name, self.newfn.args,
+ self.newfn.retvals)
+
+ self.replaceCodeWithFunctionCall(srclines, fncall,
+ startline, endline, startcol, endcol)
+
+
+ def replaceCodeWithFunctionCall(self, srclines, fncall,
+ startline, endline, startcol, endcol):
+ if startline == endline: # i.e. extracted code part of existing line
+ line = srclines[startline-1]
+ srclines[startline-1] = self.replaceSectionOfLineWithFunctionCall(line,
+ startcol, endcol, fncall)
+ else:
+ self.replaceLinesWithFunctionCall(srclines, startline, endline, fncall)
+
+
+ def replaceLinesWithFunctionCall(self, srclines, startline, endline, fncall):
+ tabwidth = getTabWidthOfLine(srclines[startline-1])
+ line = tabwidth*" " + fncall + self.linesep
+ srclines[startline-1:endline] = [line]
+
+
+
+ def replaceSectionOfLineWithFunctionCall(self, line, startcol, endcol, fncall):
+ line = line[:startcol] + fncall + line[endcol:]
+ if not line.endswith(self.linesep):
+ line+=self.linesep
+ return line
+
+
+
+ def constructFunctionCallString(self, fnname, fnargs, retvals):
+ fncall = fnname + "("+", ".join(fnargs)+")"
+ if self.isAMethod:
+ fncall = "self." + fncall
+
+ if retvals != []:
+ fncall = ", ".join(retvals) + " = "+fncall
+ return fncall
+
+
+ def deduceArguments(self):
+ lines = self.fn.getLinesNotIncludingThoseBelongingToChildScopes()
+
+ # strip off comments
+ lines = [commentRE.sub(self.linesep,line) for line in lines]
+ extractedLines = maskStringsAndRemoveComments("".join(self.extractedLines)).splitlines(1)
+
+ linesbefore = lines[:(self.startline - self.fn.getStartLine())]
+ linesafter = lines[(self.endline - self.fn.getStartLine()) + 1:]
+
+ # split into logical lines
+ linesbefore = [line for line in generateLogicalLines(linesbefore)]
+ extractedLines = [line for line in generateLogicalLines(extractedLines)]
+ linesafter = [line for line in generateLogicalLines(linesafter)]
+
+ if self.startline == self.endline:
+ # need to include the line code is extracted from
+ line = generateLogicalLines(lines[self.startline - self.fn.getStartLine():]).next()
+ linesbefore.append(line[:self.startcol] + "dummyFn()" + line[self.endcol:])
+ assigns = getAssignments(linesbefore)
+ fnargs = getFunctionArgs(linesbefore)
+ candidateArgs = assigns + fnargs
+ refs = getVariableReferencesInLines(extractedLines)
+ self.newfn.args = [ref for ref in refs if ref in candidateArgs]
+
+ assignsInExtractedBlock = getAssignments(extractedLines)
+ usesAfterNewFunctionCall = getVariableReferencesInLines(linesafter)
+ usesInPreceedingLoop = getVariableReferencesInLines(
+ self.getPreceedingLinesInLoop(linesbefore,line))
+ self.newfn.retvals = [ref for ref in usesInPreceedingLoop+usesAfterNewFunctionCall
+ if ref in assignsInExtractedBlock]
+
+ def getPreceedingLinesInLoop(self,linesbefore,firstLineToExtract):
+ if linesbefore == []: return []
+ tabwidth = getTabWidthOfLine(firstLineToExtract)
+ rootTabwidth = getTabWidthOfLine(linesbefore[0])
+ llines = [line for line in generateLogicalLines(linesbefore)]
+ startpos = len(llines)-1
+ loopTabwidth = tabwidth
+ for idx in range(startpos,0,-1):
+ line = llines[idx]
+ if re.match("(\s+)for",line) is not None or \
+ re.match("(\s+)while",line) is not None:
+ candidateLoopTabwidth = getTabWidthOfLine(line)
+ if candidateLoopTabwidth < loopTabwidth:
+ startpos = idx
+ return llines[startpos:]
+
+
+
+
+
+
+ def getRegionToBuffer(self):
+ startline = self.startline
+ endline = self.endline
+ startcol = self.startcol
+ endcol= self.endcol
+
+
+ self.extractedLines = self.sourcenode.getLines()[startline-1:endline]
+
+ match = re.match("\s*",self.extractedLines[0])
+ tabwidth = match.end(0)
+
+ self.extractedLines = [line[startcol:] for line in self.extractedLines]
+
+ # above cropping can take a blank line's newline off.
+ # this puts it back
+ for idx in range(len(self.extractedLines)):
+ if self.extractedLines[idx] == '':
+ self.extractedLines[idx] = self.linesep
+
+ if startline == endline:
+ # need to crop the end
+ # (n.b. if region is multiple lines, then whole lines are taken)
+ self.extractedLines[-1] = self.extractedLines[-1][:endcol-startcol]
+
+ if self.extractedLines[-1][-1] != '\n':
+ self.extractedLines[-1] += self.linesep
+
+ def extractedCodeIsAnExpression(self,lines):
+ if len(self.extractedLines) == 1:
+ charsBeforeSelection = lines[self.startline-1][:self.startcol]
+ if re.match("^\s*$",charsBeforeSelection) is not None:
+ return 0
+ if re.search(":\s*$",charsBeforeSelection) is not None:
+ return 0
+ return 1
+ return 0
+
+ def deduceIfIsMethodOrFunction(self):
+ if isinstance(self.fn.getParent(),Class):
+ self.isAMethod = 1
+ else:
+ self.isAMethod = 0
+
+
+# holds information about the new function
+class NewFunction:
+ def __init__(self,name):
+ self.name = name
+
+
+# lines = list of lines.
+# Have to have strings masked and comments removed
+def getAssignments(lines):
+ class AssignVisitor:
+ def __init__(self):
+ self.assigns = []
+
+ def visitAssTuple(self, node):
+ for a in node.nodes:
+ if a.name not in self.assigns:
+ self.assigns.append(a.name)
+
+ def visitAssName(self, node):
+ if node.name not in self.assigns:
+ self.assigns.append(node.name)
+
+ def visitAugAssign(self, node):
+ if isinstance(node.node, compiler.ast.Name):
+ if node.node.name not in self.assigns:
+ self.assigns.append(node.node.name)
+
+ assignfinder = AssignVisitor()
+ for line in lines:
+ doctoredline = makeLineParseable(line)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ visitor.walk(ast, assignfinder)
+ return assignfinder.assigns
+
+
+# lines = list of lines.
+# Have to have strings masked and comments removed
+def getFunctionArgs(lines):
+ if lines == []: return []
+
+ class FunctionVisitor:
+ def __init__(self):
+ self.result = []
+ def visitFunction(self, node):
+ for n in node.argnames:
+ if n != "self":
+ self.result.append(n)
+ fndef = generateLogicalLines(lines).next()
+ doctoredline = makeLineParseable(fndef)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ return visitor.walk(ast, FunctionVisitor()).result
+
+
+
+# lines = list of lines. Have to have strings masked and comments removed
+def getVariableReferencesInLines(lines):
+ class NameVisitor:
+ def __init__(self):
+ self.result = []
+ def visitName(self, node):
+ if node.name not in self.result:
+ self.result.append(node.name)
+ reffinder = NameVisitor()
+ for line in lines:
+ doctoredline = makeLineParseable(line)
+ try:
+ ast = compiler.parse(doctoredline)
+ except ParserError:
+ raise ParserException("couldnt parse:"+doctoredline)
+ visitor.walk(ast, reffinder)
+ return reffinder.result
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/extractVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,32 @@
+from bike.parsing.parserutils import maskStringsAndRemoveComments
+from bike.transformer.undo import getUndoStack
+from parser import ParserError
+import compiler
+from bike.refactor.extractMethod import coords
+from bike.refactor.utils import getTabWidthOfLine, getLineSeperator,\
+ reverseCoordsIfWrongWayRound
+from bike.transformer.save import queueFileToSave
+from bike.parsing.load import getSourceNode
+
+
+def extractLocalVariable(filename, startcoords, endcoords, varname):
+ sourceobj = getSourceNode(filename)
+ if startcoords.line != endcoords.line:
+ raise "Can't do multi-line extracts yet"
+ startcoords, endcoords = \
+ reverseCoordsIfWrongWayRound(startcoords,endcoords)
+ line = sourceobj.getLine(startcoords.line)
+ tabwidth = getTabWidthOfLine(line)
+ linesep = getLineSeperator(line)
+ region = line[startcoords.column:endcoords.column]
+
+ getUndoStack().addSource(sourceobj.filename,sourceobj.getSource())
+ sourceobj.getLines()[startcoords.line-1] = \
+ line[:startcoords.column] + varname + line[endcoords.column:]
+
+ defnline = tabwidth*" " + varname + " = " + region + linesep
+
+ sourceobj.getLines().insert(startcoords.line-1,defnline)
+
+ queueFileToSave(sourceobj.filename,"".join(sourceobj.getLines()))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/inlineVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,94 @@
+from bike.query.findDefinition import findAllPossibleDefinitionsByCoords
+from bike.query.findReferences import findReferences
+from bike.parsing.parserutils import maskStringsAndRemoveComments, linecontinueRE
+from bike.transformer.undo import getUndoStack
+from bike.transformer.save import queueFileToSave
+from parser import ParserError
+from bike.parsing.load import getSourceNode
+import compiler
+import re
+
+
+def inlineLocalVariable(filename, lineno,col):
+ sourceobj = getSourceNode(filename)
+ return inlineLocalVariable_old(sourceobj, lineno,col)
+
+def inlineLocalVariable_old(sourcenode,lineno,col):
+ definition, region, regionlinecount = getLocalVariableInfo(sourcenode, lineno, col)
+ addUndo(sourcenode)
+ replaceReferences(sourcenode, findReferences(sourcenode.filename, definition.lineno, definition.colno), region)
+ delLines(sourcenode, definition.lineno-1, regionlinecount)
+ updateSource(sourcenode)
+
+def getLocalVariableInfo(sourcenode, lineno, col):
+ definition = findDefinition(sourcenode, lineno, col)
+ region, linecount = getRegionToInline(sourcenode, definition)
+ return definition, region, linecount
+
+def findDefinition(sourcenode, lineno, col):
+ definition = findAllPossibleDefinitionsByCoords(sourcenode.filename,
+ lineno,col).next()
+ assert definition.confidence == 100
+ return definition
+
+def getRegionToInline(sourcenode, defn):
+ line, linecount = getLineAndContinues(sourcenode, defn.lineno)
+ start, end = findRegionToInline(maskStringsAndRemoveComments(line))
+ return line[start:end], linecount
+
+def findRegionToInline(maskedline):
+ match = re.compile("[^=]+=\s*(.+)$\n", re.DOTALL).match(maskedline)
+ assert match
+ return match.start(1), match.end(1)
+
+# Possible refactoring: move to class of sourcenode
+def getLineAndContinues(sourcenode, lineno):
+ line = sourcenode.getLine(lineno)
+
+ linecount = 1
+ while linecontinueRE.search(line):
+ line += sourcenode.getLine(lineno + linecount)
+ linecount += 1
+
+ return line, linecount
+
+def addUndo(sourcenode):
+ getUndoStack().addSource(sourcenode.filename,sourcenode.getSource())
+
+def replaceReferences(sourcenode, references, replacement):
+ for reference in safeReplaceOrder( references ):
+ replaceReference(sourcenode, reference, replacement)
+
+def safeReplaceOrder( references ):
+ """
+ When inlining a variable, if multiple instances occur on the line, then the
+ last reference must be replaced first. Otherwise the remaining intra-line
+ references will be incorrect.
+ """
+ def safeReplaceOrderCmp(self, other):
+ return -cmp(self.colno, other.colno)
+
+ result = list(references)
+ result.sort(safeReplaceOrderCmp)
+ return result
+
+
+def replaceReference(sourcenode, ref, replacement):
+ """ sourcenode.getLines()[ref.lineno-1][ref.colno:ref.colend] = replacement
+ But strings don't support slice assignment as they are immutable. :(
+ """
+ sourcenode.getLines()[ref.lineno-1] = \
+ replaceSubStr(sourcenode.getLines()[ref.lineno-1],
+ ref.colno, ref.colend, replacement)
+
+def replaceSubStr(str, start, end, replacement):
+ return str[:start] + replacement + str[end:]
+
+# Possible refactoring: move to class of sourcenode
+def delLines(sourcenode, lineno, linecount=1):
+ del sourcenode.getLines()[lineno:lineno+linecount]
+
+def updateSource(sourcenode):
+ queueFileToSave(sourcenode.filename,"".join(sourcenode.getLines()))
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/moveToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,148 @@
+import bike.globals
+from bike.parsing.load import getSourceNode
+from bike.parsing.fastparserast import Module
+from bike.query.common import getScopeForLine, convertNodeToMatchObject
+from bike.transformer.save import queueFileToSave, save
+from bike.transformer.undo import getUndoStack
+from bike.refactor.extractMethod import getVariableReferencesInLines
+from bike.refactor.utils import getLineSeperator
+from bike.query.findDefinition import findDefinitionFromASTNode
+from bike.query.findReferences import findReferences
+from bike.parsing.pathutils import filenameToModulePath
+from compiler.ast import Name
+import re
+
+def moveClassToNewModule(origfile,line,newfile):
+ srcnode = getSourceNode(origfile)
+ targetsrcnode = getSourceNode(newfile)
+ classnode = getScopeForLine(srcnode,line)
+ classlines = srcnode.getLines()[classnode.getStartLine()-1:
+ classnode.getEndLine()-1]
+ getUndoStack().addSource(srcnode.filename,
+ srcnode.getSource())
+ getUndoStack().addSource(targetsrcnode.filename,
+ targetsrcnode.getSource())
+
+ srcnode.getLines()[classnode.getStartLine()-1:
+ classnode.getEndLine()-1] = []
+
+ targetsrcnode.getLines().extend(classlines)
+
+ queueFileToSave(srcnode.filename,srcnode.getSource())
+ queueFileToSave(targetsrcnode.filename,targetsrcnode.getSource())
+
+
+exactFromRE = "(from\s+\S+\s+import\s+%s)(.*)"
+fromRE = "from\s+\S+\s+import\s+(.*)"
+
+def moveFunctionToNewModule(origfile,line,newfile):
+
+ srcnode = getSourceNode(origfile)
+ targetsrcnode = getSourceNode(newfile)
+ scope = getScopeForLine(srcnode,line)
+
+ linesep = getLineSeperator(srcnode.getLines()[0])
+
+ matches =[m for m in findReferences(origfile, line, scope.getColumnOfName())]
+
+ origFileImport = []
+ fromline = 'from %s import %s'%(filenameToModulePath(newfile),scope.name)
+
+ for match in matches:
+ if match.filename == origfile:
+ origFileImport = fromline + linesep
+ else:
+ s = getSourceNode(match.filename)
+ m = s.fastparseroot
+ if match.lineno in m.getImportLineNumbers():
+ getUndoStack().addSource(s.filename,
+ s.getSource())
+
+ maskedline = m.getLogicalLine(match.lineno)
+ origline = s.getLines()[match.lineno-1]
+ reMatch = re.match(exactFromRE%(scope.name),maskedline)
+ if reMatch and not (',' in reMatch.group(2) or \
+ '\\' in reMatch.group(2)):
+ # i.e. line is 'from module import foo'
+
+ if match.filename == newfile:
+ #remove the import
+ s.getLines()[match.lineno-1:match.lineno] = []
+ pass
+ else:
+ restOfOrigLine = origline[len(reMatch.group(1)):]
+ s.getLines()[match.lineno-1] = fromline + restOfOrigLine
+
+ elif re.match(fromRE,maskedline):
+ # i.e. line is 'from module import foo,bah,baz'
+ #remove the element from the import stmt
+ line = removeNameFromMultipleImportLine(scope.name, origline)
+ s.getLines()[match.lineno-1] = line
+ #and add a new line
+ nextline = match.lineno + maskedline.count('\\') + 1
+ s.getLines()[nextline-1:nextline-1] = [fromline+linesep]
+
+ queueFileToSave(s.filename,s.getSource())
+
+
+ refs = getVariableReferencesInLines(scope.getMaskedLines())
+
+ scopeLines = srcnode.getLines()[scope.getStartLine()-1:
+ scope.getEndLine()-1]
+ importModules = deduceImportsForNewFile(refs, scope)
+ importlines = composeNewFileImportLines(importModules, linesep)
+
+
+
+ getUndoStack().addSource(srcnode.filename,
+ srcnode.getSource())
+ getUndoStack().addSource(targetsrcnode.filename,
+ targetsrcnode.getSource())
+
+ srcnode.getLines()[scope.getStartLine()-1:
+ scope.getEndLine()-1] = origFileImport
+
+ targetsrcnode.getLines().extend(importlines+scopeLines)
+
+ queueFileToSave(srcnode.filename,srcnode.getSource())
+ queueFileToSave(targetsrcnode.filename,targetsrcnode.getSource())
+
+def removeNameFromMultipleImportLine(name, origline):
+ def replacefn(match):
+ return match.group(1)
+ line = re.sub('(\W)%s\s*?,'%(name),replacefn,origline)
+ return line
+
+
+
+
+def composeNewFileImportLines(importModules, linesep):
+ importlines = []
+ for mpath in importModules:
+ importlines += "from %s import %s"%(mpath,
+ ', '.join(importModules[mpath]))
+ importlines += linesep
+ return importlines
+
+def deduceImportsForNewFile(refs, scope):
+ importModules = {}
+ for ref in refs:
+ match = findDefinitionFromASTNode(scope,Name(ref))
+
+ if match.filename == scope.module.filename:
+ tgtscope = getScopeForLine(getSourceNode(match.filename),
+ match.lineno)
+ while tgtscope != scope and not isinstance(tgtscope,Module):
+ tgtscope = tgtscope.getParent()
+
+ if not isinstance(tgtscope,Module):
+ continue # was defined in this function
+
+ mpath = filenameToModulePath(match.filename)
+ if mpath in importModules:
+ importModules[mpath].append(ref)
+ else:
+ importModules[mpath] = [ref]
+ return importModules
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/rename.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,19 @@
+from bike.transformer.WordRewriter import WordRewriter
+from bike.query.findReferences import findReferencesIncludingDefn
+from bike.transformer.save import save
+
+def rename(filename,lineno,col,newname,promptcallback=None):
+ strrewrite = WordRewriter()
+ for match in findReferencesIncludingDefn(filename,lineno,col):
+ #print "rename match ",match
+ if match.confidence == 100 or promptUser(promptcallback,match):
+ strrewrite.rewriteString(match.sourcenode,
+ match.lineno,match.colno,newname)
+ strrewrite.commit()
+
+def promptUser(promptCallback,match):
+ if promptCallback is not None and \
+ promptCallback(match.filename, match.lineno, match.colno, match.colend):
+ return 1
+ return 0
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("../..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath("../.."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_extractMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,791 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+
+from bike.refactor.extractMethod import ExtractMethod, \
+ extractMethod, coords
+from bike import testdata
+from bike.testutils import *
+from bike.parsing.load import Cache
+
+def assertTokensAreSame(t1begin, t1end, tokens):
+ it = t1begin.clone()
+ pos = 0
+ while it != t1end:
+ assert it.deref() == tokens[pos]
+ it.incr()
+ pos+=1
+ assert pos == len(tokens)
+
+
+def helper(src,startcoords, endcoords, newname):
+ sourcenode = createAST(src)
+ extractMethod(tmpfile, startcoords, endcoords, newname)
+ return sourcenode.getSource()
+
+class TestExtractMethod(BRMTestCase):
+
+ def test_extractsPass(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(3, 8), coords(3, 12), "newMethod")
+ self.assertEqual(src,srcAfter)
+
+ def test_extractsPassWhenFunctionAllOnOneLine(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): pass # comment
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): self.newMethod() # comment
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(2, 24), coords(2, 28),"newMethod")
+ self.assertEqual(src,srcAfter)
+
+ def test_extractsPassFromForLoop(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(4, 12), coords(4, 16), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasArgumentsForUsedTemporarys(self):
+
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self, c):
+ a = something()
+ b = somethingelse()
+ print a + b + c + d
+ print \"hello\"
+ dosomethingelse(a, b)
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self, c):
+ a = something()
+ b = somethingelse()
+ self.newMethod(a, b, c)
+ dosomethingelse(a, b)
+
+ def newMethod(self, a, b, c):
+ print a + b + c + d
+ print \"hello\"
+ """)
+
+ src = helper(srcBefore, coords(5, 8), coords(6, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasSingleArgument(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = something()
+ print a
+ print \"hello\"
+ dosomethingelse(a, b)
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = something()
+ self.newMethod(a)
+ dosomethingelse(a, b)
+
+ def newMethod(self, a):
+ print a
+ print \"hello\"
+ """)
+ src = helper(srcBefore, coords(4, 8), coords(5, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntHaveDuplicateArguments(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 3
+ print a
+ print a
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 3
+ self.newMethod(a)
+
+ def newMethod(self, a):
+ print a
+ print a
+ """)
+ src = helper(srcBefore, coords(4, 0), coords(6, 0), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsQueryWhenFunctionAllOnOneLine(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self, a): print a # comment
+ """)
+
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self, a): self.newMethod(a) # comment
+
+ def newMethod(self, a):
+ print a
+ """)
+ src = helper(srcBefore, coords(2, 27), coords(2, 34), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_worksWhenAssignmentsToTuples(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b, c = 35, 36, 37
+ print a + b
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b, c = 35, 36, 37
+ self.newMethod(a, b)
+
+ def newMethod(self, a, b):
+ print a + b
+ """)
+
+ src = helper(srcBefore, coords(4, 8), coords(4, 19), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_worksWhenUserSelectsABlockButDoesntSelectTheHangingDedent(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+
+ src = helper(srcBefore, coords(4, 8), coords(4, 16), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+ def test_newMethodHasSingleReturnValue(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 35 # <-- extract me
+ print a
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = self.newMethod()
+ print a
+
+ def newMethod(self):
+ a = 35 # <-- extract me
+ return a
+ """)
+
+ src = helper(srcBefore, coords(3, 4),
+ coords(3, 34), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_newMethodHasMultipleReturnValues(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a = 35
+ b = 352
+ print a + b
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ a, b = self.newMethod()
+ print a + b
+
+ def newMethod(self):
+ a = 35
+ b = 352
+ return a, b
+ """)
+ src = helper(srcBefore, coords(3, 8),
+ coords(4, 15), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_worksWhenMovingCodeJustAfterDedent(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ print \"hello\"
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ for i in foo:
+ pass
+ self.newMethod()
+
+ def newMethod(self):
+ print \"hello\"
+ """)
+
+ src = helper(srcBefore, coords(5, 8),
+ coords(5, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsPassWhenSelectionCoordsAreReversed(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ self.newMethod()
+
+ def newMethod(self):
+ pass
+ """)
+ src = helper(srcBefore, coords(3, 12), coords(3, 8), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsExpression(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ a = 32
+ b = 2 + a * 1 + 2
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ a = 32
+ b = 2 + self.newMethod(a) + 2
+
+ def newMethod(self, a):
+ return a * 1
+ """)
+ src = helper(srcBefore, coords(4, 16), coords(4, 21), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsExpression2(self):
+ srcBefore=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ g = 32
+ assert output.thingy(g) == \"bah\"
+ """)
+ srcAfter=trimLines("""
+ class MyClass:
+ def myMethod(self): # comment
+ g = 32
+ assert self.newMethod(g) == \"bah\"
+
+ def newMethod(self, g):
+ return output.thingy(g)
+ """)
+ src = helper(srcBefore, coords(4, 15), coords(4, 31), "newMethod")
+ self.assertEqual(srcAfter, src)
+
+
+
+class TestExtractFunction(BRMTestCase):
+ def runTarget(self, src, begincoords, endcoords, newname):
+ ast = createAST(src)
+ extractFunction(ast, begincoords, endcoords, newname)
+ return ast
+
+ def test_extractsFunction(self):
+ srcBefore=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ c = a + 99
+ b = c * 1
+ print b
+ """)
+ srcAfter=trimLines("""
+ def myFunction(): # comment
+ a = 3
+ b = newFunction(a)
+ print b
+
+ def newFunction(a):
+ c = a + 99
+ b = c * 1
+ return b
+ """)
+
+ src = helper(srcBefore, coords(3, 4),
+ coords(4, 13), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsAssignToAttribute(self):
+ srcBefore=trimLines("""
+ def simulateLoad(path):
+ item = foo()
+ item.decl = line
+ """)
+ srcAfter=trimLines("""
+ def simulateLoad(path):
+ item = foo()
+ newFunction(item)
+
+ def newFunction(item):
+ item.decl = line
+ """)
+
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsFromFirstBlockOfIfElseStatement(self):
+ srcBefore=trimLines("""
+ def foo():
+ if bah:
+ print \"hello1\"
+ print \"hello2\"
+
+ elif foo:
+ pass
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ if bah:
+ newFunction()
+ print \"hello2\"
+
+ elif foo:
+ pass
+
+ def newFunction():
+ print \"hello1\"
+ """)
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsAugAssign(self):
+ srcBefore=trimLines("""
+ def foo():
+ a = 3
+ a += 1
+ print a
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3
+ a = newFunction(a)
+ print a
+
+ def newFunction(a):
+ a += 1
+ return a
+ """)
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractsForLoopUsingLoopVariable(self):
+ srcBefore=trimLines("""
+ def foo():
+ for i in range(1, 3):
+ print i
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ for i in range(1, 3):
+ newFunction(i)
+
+ def newFunction(i):
+ print i
+ """)
+
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractWhileLoopVariableIncrement(self):
+ srcBefore=trimLines("""
+ def foo():
+ a = 0
+ while a != 3:
+ a = a+1
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 0
+ while a != 3:
+ a = newFunction(a)
+
+ def newFunction(a):
+ a = a+1
+ return a
+ """)
+ src = helper(srcBefore, coords(4, 0),
+ coords(5, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractAssignedVariableUsedInOuterForLoop(self):
+ srcBefore=trimLines("""
+ def foo():
+ b = 0
+ for a in range(1, 3):
+ b = b+1
+ while b != 2:
+ print a
+ b += 1
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ b = 0
+ for a in range(1, 3):
+ b = b+1
+ while b != 2:
+ b = newFunction(a, b)
+
+ def newFunction(a, b):
+ print a
+ b += 1
+ return b
+ """)
+
+ src = helper(srcBefore, coords(6, 0),
+ coords(8, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_extractsConditionalFromExpression(self):
+ srcBefore=trimLines("""
+ def foo():
+ if 123+3:
+ print aoue
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ if newFunction():
+ print aoue
+
+ def newFunction():
+ return 123+3
+ """)
+ src = helper(srcBefore, coords(2, 7),
+ coords(2, 12), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ def test_extractCodeAfterCommentInMiddleOfFnDoesntRaiseParseException(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ print 1
+ # comment
+ print 2
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ print 1
+ # comment
+ newFunction()
+
+ def newFunction():
+ print 2
+ """)
+ src = helper(srcBefore, coords(4, 0),
+ coords(5, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_canExtractQueryFromNestedIfStatement(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ if foo: # comment
+ if bah:
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ if foo: # comment
+ if newFunction():
+ pass
+
+ def newFunction():
+ return bah
+ """)
+ src = helper(srcBefore, coords(3, 11),
+ coords(3, 14), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+
+ def test_doesntMessUpTheNextFunctionOrClass(self):
+ srcBefore=trimLines("""
+ def myFunction():
+ a = 3
+ print \"hello\"+a # extract me
+
+ class MyClass:
+ def myMethod(self):
+ b = 12 # extract me
+ c = 3 # and me
+ d = 2 # and me
+ print b, c
+ """)
+ srcAfter=trimLines("""
+ def myFunction():
+ a = 3
+ newFunction(a)
+
+ def newFunction(a):
+ print \"hello\"+a # extract me
+
+ class MyClass:
+ def myMethod(self):
+ b = 12 # extract me
+ c = 3 # and me
+ d = 2 # and me
+ print b, c
+ """)
+
+ # extract code on one line
+ src = helper(srcBefore, coords(3, 4),
+ coords(3, 34), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+ # extract code on 2 lines (most common user method)
+ resetRoot()
+ Cache.instance.reset()
+ Root()
+ src = helper(srcBefore, coords(3, 0),
+ coords(4, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntBallsUpIndentWhenTheresALineWithNoSpacesInIt(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ if 1:
+ pass
+
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ newFunction()
+
+ def newFunction():
+ if 1:
+ pass
+
+ pass
+ """)
+ src = helper(srcBefore, coords(2, 4),
+ coords(5, 8), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntHaveToBeInsideAFunction(self):
+ srcBefore=trimLines(r"""
+ a = 1
+ print a + 2
+ f(b)
+ """)
+ srcAfter=trimLines(r"""
+ a = 1
+ newFunction(a)
+
+ def newFunction(a):
+ print a + 2
+ f(b)
+ """)
+ src = helper(srcBefore, coords(2, 0),
+ coords(3, 4), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_doesntBarfWhenEncountersMethodCalledOnCreatedObj(self):
+ srcBefore=trimLines(r"""
+ results = QueryEngine(q).foo()
+ """)
+ srcAfter=trimLines(r"""
+ newFunction()
+
+ def newFunction():
+ results = QueryEngine(q).foo()
+ """)
+ src = helper(srcBefore, coords(1, 0),
+ coords(2, 0), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+ def test_worksIfNoLinesBeforeExtractedCode(self):
+ srcBefore=trimLines(r"""
+ print a + 2
+ f(b)
+ """)
+ srcAfter=trimLines(r"""
+ newFunction()
+
+ def newFunction():
+ print a + 2
+ f(b)
+ """)
+ src = helper(srcBefore, coords(1, 0),
+ coords(2, 4), "newFunction")
+ self.assertEqual(srcAfter, src)
+
+
+class TestGetRegionAsString(BRMTestCase):
+ def test_getsHighlightedSingleLinePassStatement(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 8),
+ coords(3, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(len(em.extractedLines), 1)
+ self.assertEqual(em.extractedLines[0], "pass\n")
+
+ def test_getsSingleLinePassStatementWhenWholeLineIsHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 0),
+ coords(3, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(len(em.extractedLines), 1)
+ self.assertEqual(em.extractedLines[0], "pass\n")
+
+
+ def test_getsMultiLineRegionWhenJustRegionIsHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ print 'hello'
+ pass
+ """)
+ region=trimLines("""
+ print 'hello'
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 8),
+ coords(4, 12), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+ def test_getsMultiLineRegionWhenRegionLinesAreHighlighted(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ print 'hello'
+ pass
+
+ """)
+ region=trimLines("""
+ print 'hello'
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 0),
+ coords(5, 0), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+ def test_getsHighlightedSubstringOfLine(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ if a == 3:
+ pass
+ """)
+ region=trimLines("""
+ a == 3
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 11),
+ coords(3, 17), "foobah")
+ em.getRegionToBuffer()
+ self.assertEqual(em.extractedLines, region.splitlines(1))
+
+
+class TestGetTabwidthOfParentFunction(BRMTestCase):
+ def test_getsTabwidthForSimpleMethod(self):
+ src=trimLines("""
+ class MyClass:
+ def myMethod(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(3, 11),
+ coords(3, 17), "foobah")
+ self.assertEqual(em.getTabwidthOfParentFunction(), 4)
+
+ def test_getsTabwidthForFunctionAtRootScope(self):
+ src=trimLines("""
+ def myFn(self):
+ pass
+ """)
+ sourcenode = createAST(src)
+ em = ExtractMethod(sourcenode, coords(2, 0),
+ coords(2, 9), "foobah")
+ self.assertEqual(em.getTabwidthOfParentFunction(), 0)
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_extractVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike.testutils import *
+from bike.refactor.extractVariable import coords, extractLocalVariable
+
+class TestExtractLocalVariable(BRMTestCase):
+ def test_worksOnSimpleCase(self):
+ srcBefore=trimLines("""
+ def foo():
+ print 3 + 2
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3 + 2
+ print a
+ """)
+ sourcenode = createAST(srcBefore)
+ extractLocalVariable(tmpfile,coords(2,10),coords(2,15),'a')
+ self.assertEqual(sourcenode.getSource(),srcAfter)
+
+ def test_worksIfCoordsTheWrongWayRound(self):
+ srcBefore=trimLines("""
+ def foo():
+ print 3 + 2
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ a = 3 + 2
+ print a
+ """)
+ sourcenode = createAST(srcBefore)
+ extractLocalVariable(tmpfile,coords(2,15),coords(2,10),'a')
+ self.assertEqual(sourcenode.getSource(),srcAfter)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_inlineVariable.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike.testutils import trimLines,createAST, BRMTestCase
+from inlineVariable import inlineLocalVariable_old
+
+class TestInlineLocalVariable(BRMTestCase):
+
+ def test_worksWhenUserDoesItAgainstReference(self):
+ srcBefore=r"""
+ def foo():
+ b = 'hello'
+ print b
+ """
+ srcAfter=r"""
+ def foo():
+ print 'hello'
+ """
+
+ self.helper( srcBefore, 3, 10, srcAfter )
+
+ def test_worksWhenInlinedCodeIsOverTwoLines(self):
+ srcBefore=r"""
+ def foo():
+ b = 3 + \
+ 2
+ print b
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 3 + \
+ 2
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ ''' Needs Adding Again
+ def test_addsBracketsWhenInlinedCodeHasPresidenceOverSurroundingCode(self):
+ srcBefore=trimLines(r"""
+ def foo():
+ b = 3 + 2
+ print 3 * b
+ """)
+ srcAfter=trimLines(r"""
+ def foo():
+ print 3 * (3 + 2)
+ """)
+ assert 0
+ '''
+
+ def test_worksWithMultipleInstancesOfVariableOnLine(self):
+ srcBefore=r"""
+ def foo():
+ x = 11
+ print x, x
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 11, 11
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ def test_worksWithMultipleMultilineCode(self):
+ srcBefore=r"""
+ def foo():
+ b = 3 + \
+ 2
+ print b
+ print b
+ """
+
+ srcAfter=r"""
+ def foo():
+ print 3 + \
+ 2
+ print 3 + \
+ 2
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+
+ ''' Can't do this without some hairy logic to deduce how to inline
+ the variables. E.g. how do you inline a,b = foo() ?
+
+ def test_handlesTupleAssignment(self):
+ srcBefore=r"""
+ def foo():
+ x, y = 1, 2
+ print x
+ print y
+ """
+
+ srcAfter=r"""
+ def foo():
+ y = 2
+ print 1
+ print y
+ """
+
+ self.helper(srcBefore, 2, 4, srcAfter)
+ '''
+
+
+ def helper(self, srcBefore, y, x, srcAfter):
+ sourcenode = createAST(trimLines(srcBefore))
+ inlineLocalVariable_old(sourcenode,y,x)
+ self.assertEqual(sourcenode.getSource(),trimLines(srcAfter))
+
+if __name__ == "__main__":
+ unittest.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_moveToModule.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+import setpath
+from bike.testutils import *
+from bike.transformer.save import save
+
+from moveToModule import *
+
+class TestMoveClass(BRMTestCase):
+ def test_movesTheText(self):
+ src1=trimLines("""
+ def before(): pass
+ class TheClass:
+ pass
+ def after(): pass
+ """)
+ src1after=trimLines("""
+ def before(): pass
+ def after(): pass
+ """)
+ src2after=trimLines("""
+ class TheClass:
+ pass
+ """)
+
+ try:
+ createPackageStructure(src1, "")
+ moveClassToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src1after,file(pkgstructureFile1).read())
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+class TestMoveFunction(BRMTestCase):
+ def test_importsNameReference(self):
+ src1=trimLines("""
+ a = 'hello'
+ def theFunction(self):
+ print a
+ """)
+ src2after=trimLines("""
+ from a.foo import a
+ def theFunction(self):
+ print a
+ """)
+ self.helper(src1, src2after)
+
+
+
+ def test_importsExternalReference(self):
+ src0=("""
+ a = 'hello'
+ """)
+ src1=trimLines("""
+ from top import a
+ def theFunction(self):
+ print a
+ """)
+ src2after=trimLines("""
+ from top import a
+ def theFunction(self):
+ print a
+ """)
+ try:
+ createPackageStructure(src1, "", src0)
+ moveFunctionToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+ def test_doesntImportRefCreatedInFunction(self):
+ src1=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+ src2after=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+
+ self.helper(src1, src2after)
+
+
+ def test_doesntImportRefCreatedInFunction(self):
+ src1=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+ src2after=trimLines("""
+ def theFunction(self):
+ a = 'hello'
+ print a
+ """)
+
+ self.helper(src1, src2after)
+
+
+ def test_addsImportStatementToOriginalFileIfRequired(self):
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ b = theFunction()
+ """)
+
+ src1after=trimLines("""
+ from a.b.bah import theFunction
+ b = theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"")
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src1after,file(pkgstructureFile1).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportStatementsInOtherModules(self):
+ src0=trimLines("""
+ from a.foo import theFunction
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ """)
+
+ src0after=trimLines("""
+ from a.b.bah import theFunction
+ print theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportMultiplesInOtherModules(self):
+ src0=trimLines("""
+ from a.foo import something,theFunction,somethingelse #comment
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ something = ''
+ somethingelse = 0
+ """)
+
+ src0after=trimLines("""
+ from a.foo import something,somethingelse #comment
+ from a.b.bah import theFunction
+ print theFunction()
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+ def test_updatesFromImportMultiplesInTargetModule(self):
+ src0=trimLines("""
+ from a.foo import something,theFunction,somethingelse #comment
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ something = ''
+ somethingelse = 0
+ """)
+
+ src0after=trimLines("""
+ from a.foo import something,somethingelse #comment
+ print theFunction()
+ def theFunction(self):
+ pass
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile0)
+ save()
+ #print file(pkgstructureFile0).read()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+
+ def test_updatesFromImportInTargetModule(self):
+ src0=trimLines("""
+ from a.foo import theFunction
+ print theFunction()
+ """)
+ src1=trimLines("""
+ def theFunction(self):
+ pass
+ """)
+
+ src0after=trimLines("""
+ print theFunction()
+ def theFunction(self):
+ pass
+ """)
+ try:
+ createPackageStructure(src1,"",src0)
+ moveFunctionToNewModule(pkgstructureFile1,1,
+ pkgstructureFile0)
+ save()
+ self.assertEqual(src0after,file(pkgstructureFile0).read())
+ finally:
+ removePackageStructure()
+
+
+
+ def helper(self, src1, src2after):
+ try:
+ createPackageStructure(src1, "")
+ moveFunctionToNewModule(pkgstructureFile1,2,
+ pkgstructureFile2)
+ save()
+ self.assertEqual(src2after,file(pkgstructureFile2).read())
+ finally:
+ removePackageStructure()
+
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_rename.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from bike import testdata
+from rename import rename
+from bike.testutils import *
+
+class TestRenameTemporary(BRMTestCase):
+ def test_renamesSimpleReferencesGivenAssignment(self):
+ src=trimLines("""
+ def foo():
+ a = 3
+ print a
+ """)
+ srcAfter=trimLines("""
+ def foo():
+ b = 3
+ print b
+ """)
+ src = self.helper(src,"",2,4,"b")
+ self.assertEqual(srcAfter,src)
+
+ def helper(self, src, classsrc, line, col, newname):
+ try:
+ createPackageStructure(src,classsrc)
+ filename = pkgstructureFile1
+ rename(filename,line,col,newname)
+ # modify me once save is moved
+ #return readFile(filename)
+ from bike.transformer.save import outputqueue
+ return outputqueue[filename]
+ finally:
+ removePackageStructure()
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameClass.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+from bike.transformer.save import save
+from bike.testutils import *
+import compiler
+
+class RenameClassTests:
+
+ def testRenamesClassDcl(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ def theMethod():
+ pass
+ """)
+
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass()
+ def testRenamesClassReference(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a = NewName()
+ """)
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass.TheClass()
+ def testRenamesClassReferenceWhenScopeIsSameNameAsClass(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ class NewName:
+ pass
+ a = TheClass.NewName()
+ """)
+ src = self.rename(srcBefore, 2,10, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. a = TheClass.TheClass()
+ def testRenamesClassReferenceWhenChildIsSameNameAsClass(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ class TheClass:
+ pass
+ a = TheClass.TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ class TheClass:
+ pass
+ a = NewName.TheClass()
+ """)
+ src = self.rename(srcBefore, 1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+
+ # a = TheClass() + TheClass()
+ def testRenamesClassReferenceWhenTwoRefsInTheSameLine(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a = TheClass() + TheClass()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a = NewName() + NewName()
+ """)
+ src = self.rename(srcBefore,1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferenceInInstanceCreation(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ def theMethod(self): pass
+ NewName().theMethod()
+ """)
+ src = self.rename(srcBefore,1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ # i.e. if renaming TheClass, shouldnt rename a.b.c.TheClass
+ def testDoesntRenameBugusClassReferenceOnEndOfGetattrNest(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ a.b.c.TheClass # Shouldn't be renamed
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ a.b.c.TheClass # Shouldn't be renamed
+ """)
+ src = self.rename(srcBefore,1,6,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassRefUsedInExceptionRaise(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ raise TheClass, \"hello mum\"
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ raise NewName, \"hello mum\"
+ """)
+ src = self.rename(srcBefore, 1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferenceNameInInheritenceSpec(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ pass
+ class DerivedClass(TheClass):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class NewName:
+ pass
+ class DerivedClass(NewName):
+ pass
+ """)
+ src = self.rename(srcBefore, 1,6, "NewName")
+ self.assertEqual(srcAfter,src)
+
+
+
+class RenameClassTests_importsClass:
+
+ def testRenamesClassReferenceInInstanceCreationWithFQN(self):
+ srcBefore=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.NewName()
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesClassReferencesInInheritenceSpecs(self):
+
+ srcBefore=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ """)
+ srcAfter=trimLines("""
+ import b
+ class DerivedClass(b.bah.NewName):
+ pass
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFromImportReferenceWhenInBodyOfClass(self):
+ srcBefore=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ srcAfter=trimLines("""
+ class AnotherClass:
+ from b.bah import NewName
+ NewName.baz = 0
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+
+ def testRenamesReferenceToClassImportedInSameClassScope(self):
+ srcBefore=trimLines("""
+ class AnotherClass:
+ from b.bah import TheClass
+ TheClass.baz = 0
+ """)
+ srcAfter=trimLines("""
+ class AnotherClass:
+ from b.bah import NewName
+ NewName.baz = 0
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesReferenceToClassImportedWithFromImportStar(self):
+ srcBefore=trimLines("""
+ from a.b.bah import *
+ a = TheClass()
+ """)
+ srcAfter=trimLines("""
+ from a.b.bah import *
+ a = NewName()
+ """)
+ src = self.renameClass(srcBefore,"NewName")
+ self.assertEqual(srcAfter,src)
+
+class TestRenameClass(BRMTestCase, RenameClassTests):
+
+ def rename(self, src, line, col, newname):
+ createPackageStructure(src,"pass")
+ rename(pkgstructureFile1,line,col, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+class TestRenameClassReferenceWithDirectoryStructure(BRMTestCase,
+ RenameClassTests_importsClass):
+
+ def renameClass(self, src, newname):
+ createPackageStructure(src,TheClassTestdata)
+ rename(pkgstructureFile2,1,6, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+TheClassTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameFunction.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+from bike import testdata
+from bike.testutils import *
+from bike.transformer.save import save
+import compiler
+
+class RenameFunctionTests:
+ def runTarget(self, src, klassfqn, newname):
+ # see concrete subclasses for implementation
+ pass
+
+ def testRenamesFunctionDcl(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ pass
+ """)
+ srcAfter=trimLines("""
+ def newName():
+ pass
+ """)
+ src = self.rename(srcBefore,1,4,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testDoesntBarfWhenFunctionIncludesBrackettedExpression(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ return ('\\n').strip()
+ """)
+ srcAfter=trimLines("""
+ def newName():
+ return ('\\n').strip()
+ """)
+ src = self.rename(srcBefore,1,4, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+
+class RenameFunctionTests_importsFunction:
+
+ def testRenamesImportedFunctionReference(self):
+ srcBefore=trimLines("""
+ import b.bah
+ b.bah.theFunction()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ b.bah.newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFunctionReferenceImportedWithFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import theFunction
+ theFunction()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import newName
+ newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def testRenamesFunctionRefInImportClause(self):
+ srcBefore=trimLines("""
+ import b.bah
+ b.bah.theFunction()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ b.bah.newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def testRenamesFunctionRefInImportFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import theFunction
+ theFunction()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import newName
+ newName()
+ """)
+ src = self.renameFunction(srcBefore,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+class TestRenameFunction(BRMTestCase, RenameFunctionTests):
+ def rename(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ rename(tmpfile,line,col, newname)
+ save()
+ return file(tmpfile).read()
+
+
+class TestRenameFunctionReferenceWithDirectoryStructure(BRMTestCase, RenameFunctionTests_importsFunction):
+
+ def renameFunction(self, src, newname):
+ createPackageStructure(src,FunctionTestdata)
+ rename(pkgstructureFile2,1,4, newname)
+ save()
+ return file(pkgstructureFile1).read()
+
+FunctionTestdata = trimLines("""
+def theFunction():
+ pass
+""")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/test_renameMethod.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,710 @@
+#!/usr/bin/env python
+import setpath
+import unittest
+from rename import rename
+import compiler
+from bike import testdata
+
+from bike.testutils import*
+
+from bike.transformer.save import save
+
+class RenameMethodTests:
+
+ def test_renamesTheMethod(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameMethodOfSameNameOnOtherClasses(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ class b:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ class b:
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameOtherMethodsOfSameClass(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ a=b
+ def aMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ a=b
+ def aMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodWhenClassNestedInFunction(self):
+ srcBefore=trimLines("""
+ def theFunction():
+ class TheClass:
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ def theFunction():
+ class TheClass:
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,3,12,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnInheritanceHierarchies(self):
+ srcBefore=trimLines("""
+ from b.bah import DifferentClass
+ class TheClass(foo.bah):
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+
+ def test_renamesMethodWhenMethodCallFromOtherMethodInSameClass(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ def anotherMethod(self):
+ self.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ def anotherMethod(self):
+ self.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnNestedClasses(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ class AnotherClass:
+ pass
+ def theMethod(self):
+ pass
+ """)
+ src = self.rename(srcBefore,4,8,"newName")
+
+ def test_renamesMethodWhenBaseClassesArentInAST(self):
+ srcBefore=trimLines("""
+ class TheClass(notInAst):
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class TheClass(notInAst):
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodInRelatedClasses(self):
+ srcBefore=trimLines("""
+ class root:
+ def theMethod(self):
+ pass
+
+ class a(root):
+ def theMethod(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def theMethod(self):
+ pass
+ """)
+ srcAfter=trimLines("""
+ class root:
+ def newName(self):
+ pass
+
+ class a(root):
+ def newName(self):
+ pass
+
+ class b(root):
+ pass
+
+ class TheClass(b):
+ def newName(self):
+ pass
+ """)
+ src = self.rename(srcBefore,13,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renameMethodDoesntBarfOnNoneAsDefaultArgToMethod(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self, root, flist, stack=None):
+ pass
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+
+
+
+class RenameMethodTests_ImportsClass:
+ def test_renamesMethodOnDerivedClassInstance(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ class DerivedDerivedClass(DerivedClass):
+ def theMethod(self):
+ print 'hello'
+ """)
+ srcAfter = trimLines("""
+ from b.bah import TheClass as BaseClass
+
+ class DerivedClass(BaseClass):
+ pass
+
+ class DerivedDerivedClass(DerivedClass):
+ def newName(self):
+ print 'hello'
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+class RenameMethodReferenceTests:
+ # Generic tests. These tests are designed to be run in the context of a ui
+ # and in a package hierarchy structure
+
+ def test_doesntBarfWhenConfrontedWithComplexReturnTypes(self):
+ src = trimLines("""
+ import a
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ def bah():
+ return a[35]
+
+ b = bah()
+ b.theMethod()
+ """)
+ self.rename(src,3,8,"newName")
+
+ def test_doesntbarfWhenCallMadeOnInstanceReturnedFromFnCall(self):
+ srcBefore=trimLines("""
+ from foo import e
+ class TheClass:
+ def theMethod(self):
+ pass
+ ast = e().f(src)
+ """)
+ self.rename(srcBefore,3,8,"newName")
+
+ def test_doesntStackOverflowOnRecursiveFunctions(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ def foo(a):
+ return foo(a)
+ """)
+ self.rename(srcBefore,2,8,"newName")
+
+ def test_renamesMethodReferenceOfInstanceCreatedInParentScopeAfterFunction(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass()
+ def foo():
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ a = TheClass()
+ def foo():
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceObtainedByCallingFunction(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ def foo():
+ b = TheClass()
+ return b
+ a = foo()
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInAnotherFunction(self):
+
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ def bah():
+ return TheClass()
+ def foo():
+ a = bah()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ def bah():
+ return TheClass()
+ def foo():
+ a = bah()
+ a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInSubsequentFunction(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.theMethod()
+
+ def bah():
+ return TheClass()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName():
+ pass
+ class NotTheClass:
+ def theMethod():
+ pass
+
+ def foo():
+ a = bah()
+ a.newName()
+
+ def bah():
+ return TheClass()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOnInstanceThatIsAnAttributeOfSelf(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+
+ class AnotherClass:
+ def __init__(self):
+ self.a = TheClass()
+ def anotherFn(self):
+ self.a.newName()
+ """)
+ src = self.rename(srcBefore,2,8,"newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntBarfOnGetattrThatItCantDeduceTypeOf(self):
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ a = TheClass
+
+ a.b.bah = 3
+ """)
+ self.rename(srcBefore,2,8,"newName")
+
+
+class RenameMethodReferenceTests_ImportsClass:
+
+ def test_renamesReferenceOfClassImportedAsAnotherName(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass as MyTheClass
+ def foo():
+ a = MyTheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore,2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesReferenceWhenObjectCreationAndReferenceInModuleScope(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renamesReferenceWhenObjectCreatedInSameFunctionAsReference(self):
+ srcBefore=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+ def foo():
+ a = b.bah.TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntrenameDifferentMethodReferenceWhenObjectCreatedInSameScope(self):
+ srcBefore=trimLines("""
+ import b.bah.TheClass
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 4,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_doesntrenameMethodReferenceWhenDifferentObjectCreatedInSameScope(self):
+ srcBefore=trimLines("""
+ import b.bah.TheClass
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 8,8,"newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfImportedClass(self):
+ srcBefore=trimLines("""
+ import b.bah
+
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b.bah
+
+ def foo():
+ a = b.bah.TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameReferenceOfDifferentImportedClass(self):
+ srcBefore=trimLines("""
+ from b.bah import DifferentClass
+
+ def foo():
+ a = b.bah.TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 8,8,
+ "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfClassImportedWithFromClause(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+
+ def foo():
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntrenameReferenceOfClassImportedWithDifferentAsClause(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass as MyClass
+
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOfClassImportedWithFromFooImportStar(self):
+ srcBefore=trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import *
+ a = TheClass()
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_renamesMethodReferenceOfInstanceCreatedInParentScope(self):
+ srcBefore=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ def foo():
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ from b.bah import TheClass
+ a = TheClass()
+ def foo():
+ a.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+ def test_doesntRenameMethodWhenObjectCreatedInChildScopeToMethodReference(self):
+ srcBefore = trimLines("""
+ from b.bah import TheClass
+ a = AnotherClass()
+ def foo():
+ a = TheClass()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcBefore,src)
+
+ def test_renamesReferenceOnDerivedClassInstance(self):
+ srcBefore=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ theInstance = DerivedDerivedClass()
+ theInstance.theMethod()
+ """)
+ srcAfter=trimLines("""
+ import b
+ class DerivedClass(b.bah.TheClass):
+ pass
+ class DerivedDerivedClass(DerivedClass):
+ pass
+ theInstance = DerivedDerivedClass()
+ theInstance.newName()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+
+# tests that cover stuff not renamed automatically
+# (I.e. are renamed after user manually expresses desire to do so)
+class RenameMethodAfterPromptTests:
+ def test_renamesReferenceWhenMethodCallDoneOnInstanceCreation(self):
+
+ srcBefore=trimLines("""
+ class TheClass:
+ def theMethod(self): pass
+ TheClass().theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self): pass
+ TheClass().newName()
+ """)
+ src = self.renameMethod(srcBefore,2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+ def test_renamesReferenceInMiddleOfBiggerCompoundCall(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self): return AnotherClass()
+ TheClass().theMethod().anotherMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self): return AnotherClass()
+ TheClass().newName().anotherMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+class TestRenameMethodWithSingleModule(BRMTestCase, RenameMethodTests, RenameMethodReferenceTests):
+ # template method
+ def rename(self, src, line, col, newname):
+ try:
+ createPackageStructure(src, "pass")
+ rename(pkgstructureFile1,line,col,newname)
+ save()
+ return file(pkgstructureFile1).read()
+ finally:
+ removePackageStructure()
+
+
+class TestRenameMethodWithDirectoryStructure(RenameMethodTests, RenameMethodReferenceTests, BRMTestCase):
+
+ def rename(self, src, line, col, newname):
+ try:
+ createPackageStructure("pass",src)
+ rename(pkgstructureFile2,line,col,newname)
+ save()
+ return file(pkgstructureFile2).read()
+ finally:
+ removePackageStructure()
+
+
+class TestRenameMethodReferenceWithDirectoryStructure(BRMTestCase, RenameMethodTests_ImportsClass, RenameMethodReferenceTests_ImportsClass):
+
+ def renameMethod(self, src, line, col, newname):
+ try:
+ createPackageStructure(src,MethodTestdata)
+ rename(pkgstructureFile2,line,col,newname)
+ save()
+ return file(pkgstructureFile1).read()
+ finally:
+ removePackageStructure()
+
+class TestRenameMethodStuffCorrectlyAfterPromptReturnsTrue(BRMTestCase,
+ RenameMethodAfterPromptTests):
+
+ def callback(self, filename, line, colbegin, colend):
+ return 1
+
+
+ def renameMethod(self, src, line, col, newname):
+ createPackageStructure(src, MethodTestdata)
+ rename(pkgstructureFile1,line,col,newname,self.callback)
+ save()
+ return file(pkgstructureFile1).read()
+
+
+
+class TestDoesntRenameMethodIfPromptReturnsFalse(BRMTestCase):
+ def callback(self, filename, line, colbegin, colend):
+ return 0
+
+ def renameMethod(self, src, line, col, newname):
+ createPackageStructure(src, MethodTestdata)
+ rename(pkgstructureFile1,line,col,newname,self.callback)
+ save()
+ return file(pkgstructureFile1).read()
+
+ def test_doesntRenameMethodIfPromptReturnsFalse(self):
+ srcBefore = trimLines("""
+ class TheClass:
+ def theMethod(self):
+ pass
+ b = TheClass()
+ b.theMethod()
+ a = someFunction()
+ a.theMethod()
+ """)
+ srcAfter=trimLines("""
+ class TheClass:
+ def newName(self):
+ pass
+ b = TheClass()
+ b.newName()
+ a = someFunction()
+ a.theMethod()
+ """)
+ src = self.renameMethod(srcBefore, 2,8, "newName")
+ self.assertEqual(srcAfter,src)
+
+
+MethodTestdata = trimLines("""
+class TheClass:
+ def theMethod(self):
+ pass
+ def differentMethod(self):
+ pass
+
+class DifferentClass:
+ def theMethod(self):
+ pass
+""")
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/testall.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+import setpath
+from test_renameMethod import *
+from test_renameClass import *
+from test_renameFunction import *
+from test_rename import *
+from test_extractMethod import *
+from test_inlineVariable import *
+from test_extractVariable import *
+from test_moveToModule import *
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/refactor/utils.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,24 @@
+import re
+
+def getLineSeperator(line):
+ if line.endswith("\r\n"):
+ linesep = "\r\n" # windoze
+ else:
+ linesep = line[-1] # mac or unix
+ return linesep
+
+
+def getTabWidthOfLine(line):
+ match = re.match("\s+",line)
+ if match is None:
+ return 0
+ else:
+ return match.end(0)
+
+def reverseCoordsIfWrongWayRound(startcoords,endcoords):
+ if(startcoords.line > endcoords.line) or \
+ (startcoords.line == endcoords.line and \
+ startcoords.column > endcoords.column):
+ return endcoords,startcoords
+ else:
+ return startcoords,endcoords
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/setpath.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,5 @@
+import sys,os
+if not os.path.abspath("..") in sys.path:
+ from bike import log
+ print >> log.warning, "Appending to the system path. This should only happen in unit tests"
+ sys.path.append(os.path.abspath(".."))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/sadness/bike/bike/test_bikefacade.py Mon Nov 22 14:32:21 2010 -0500
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+import unittest
+import setpath
+import sys
+
+from bike import testdata
+from bike.testutils import *
+import bike
+from bike.refactor.test_renameFunction import RenameFunctionTests, RenameFunctionTests_importsFunction, FunctionTestdata
+from bike.refactor.test_renameClass import RenameClassTests, RenameClassTests_importsClass, TheClassTestdata
+from bike.refactor.test_renameMethod import RenameMethodTests, RenameMethodTests_ImportsClass, RenameMethodReferenceTests, RenameMethodReferenceTests_ImportsClass, RenameMethodAfterPromptTests, TestDoesntRenameMethodIfPromptReturnsFalse,MethodTestdata
+from bike.refactor import test_extractMethod
+import bikefacade
+from bike import UndoStackEmptyException
+from bike.query.getTypeOf import getTypeOf
+
+class TestPathFunctions(BRMTestCase):
+ def test_setCompletePythonPath_removesDuplicates(self):
+ origpath = sys.path
+ try:
+ sys.path = ["foobah"]
+ ctx = bike.init()
+ ctx._setCompletePythonPath(sys.path[-1])
+ self.assertEqual(1,ctx._getCurrentSearchPath().count(sys.path[-1]))
+ finally:
+ sys.path = origpath
+
+
+ def test_setNonLibPathonPath_removesLibDirectories(self):
+ origpath = sys.path
+ try:
+ writeTmpTestFile("pass")
+ libdir = os.path.join(sys.prefix,"lib","python"+sys.version[:3])
+ sys.path = [libdir,os.path.join(libdir,"site-packages")]
+ ctx = bike.init()
+ ctx._setNonLibPythonPath(tmproot)
+ self.assertEqual([tmproot],ctx._getCurrentSearchPath())
+ finally:
+ sys.path = origpath
+
+class TestRenameMethodAfterPrompt(BRMTestCase,RenameMethodAfterPromptTests):
+ def callback(self, filename, line, colstart, colend):
+ return 1
+
+ def renameMethod(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.setRenameMethodPromptCallback(self.callback)
+ ctx.renameByCoordinates(tmpfile,line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+class TestDoesntRenameMethodIfPromptReturnsFalse(TestDoesntRenameMethodIfPromptReturnsFalse):
+
+ def callback(self, filename, line, colstart, colend):
+ return 0
+
+ def renameMethod(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.setRenameMethodPromptCallback(self.callback)
+ ctx.renameByCoordinates(tmpfile,line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+
+class TestRenameByCoordinates2(RenameMethodTests,RenameMethodReferenceTests, RenameClassTests,RenameFunctionTests,BRMTestCase):
+ def rename(self, src, line, col, newname):
+ writeTmpTestFile(src)
+ ctx = bike.init()
+ ctx.renameByCoordinates(os.path.abspath(tmpfile),line,col,newname)
+ ctx.save()
+ newsrc = readFile(tmpfile)
+ return newsrc
+
+
+class TestRenameByCoordinatesWithDirectoryStructure(
+ RenameClassTests_importsClass,
+ RenameFunctionTests_importsFunction,
+ RenameMethodTests_ImportsClass,
+ RenameMethodReferenceTests_ImportsClass,
+ BRMTestCase):
+ def renameClass(self, src, newname):
+ try:
+ createPackageStructure(src, TheClassTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,1,6,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+
+ def renameMethod(self, src, line, col, newname):
+ try:
+ createPackageStructure(src, MethodTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,line,col,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+ def renameFunction(self, src, newname):
+ try:
+ createPackageStructure(src, FunctionTestdata)
+ ctx = bike.init()
+ ctx.renameByCoordinates(pkgstructureFile2,1,4,newname)
+ ctx.save()
+ newsrc = readFile(pkgstructureFile1)
+ return newsrc
+ finally:
+ removePackageStructure()
+
+
+
+class Test_deducePackageOfFile(BRMTestCase):
+ def test_returnsEmptyStringIfFileNotInPackage(self):
+ try:
+ # this doesnt have __init__.py file, so
+ # isnt package
+ os.makedirs("a")
+ writeFile(os.path.join("a","foo.py"),"pass")
+ pkg = bikefacade._deducePackageOfFile(os.path.join("a","foo.py"))
+ assert pkg == ""
+ finally:
+ os.remove(os.path.join("a","foo.py"))
+ os.removedirs(os.path.join("a"))
+
+ def test_returnsNestedPackage(self):
+ try:
+ os.makedirs(os.path.join("a","b"))
+ writeFile(os.path.join("a","__init__.py"),"# ")
+ writeFile(os.path.join("a","b","__init__.py"),"# ")
+ writeFile(os.path.join("a","b","foo.py"),"pass")
+ pkg = bikefacade._deducePackageOfFile(os.path.join("a","b","foo.py"))
+ assert pkg == "a.b"
+ finally:
+ os.remove(os.path.join("a","__init__.py"))
+ os.remove(os.path.join("a","b","__init__.py"))
+ os.remove(os.path.join("a","b","foo.py"))