Snapshot idea/138.1283 from git://git.jetbrains.org/idea/community.git
047cbb9: IDEA-127620 - Clouds: move frameworks to the top level in new module/project wizard - refactoring of import
1d1914f: IDEA-127620 - Clouds: move frameworks to the top level in new module/project wizard - remove from JavaEE group
3206e15: Merge remote-tracking branch 'origin/master'
a513e70: enable injected context for actions in action popups (IDEA-126995)
70c5d69: CommentInjector: improved detection and IDEA-114915 InjectorUtils performance
482cdc2: @MagicConstant added
997dc0b: do not show VM names in run configurations: junit, application (IDEA-126880)
9873768: merge commands to replace duplicates (IDEA-126776)
e9a17df: field can be local: when field is used in method for read it can't be converted to local if used in another methods (IDEA-127159)
f9e6f06: faster LexerEditorHighlighter.checkContentIsEqualTo
6051f49: generic debugger settings: step 3 — root settings (Debugger node content) — Java transformed
bcb94d7: cleanup
a12ac6c: treat Diagnostic.Kind.NOTE as information message, not a warning message (IDEA-105383)
f367dcb: IDEA-127666 Add tooltip to "Project Setting" icon
08d9677: add delegate method to TextFieldWithHistoryWitBrowseButton
ce0d23f: change messages for postfix templates configurable
d7d7e88: platform: syntax compatibility with antiquated Python versions for environment loader
2a10168: SwingHelper moved to platform-impl
0299b30: auto-import context variable declaration type when completing (IDEA-90157)
e353c2f: prefer some well-known java util classes in completion (IDEA-100669)
f81e96e: java chained completion: qualifier class items shouldn't be inserted as constructors
7cb41f8: Update image to visualize the project-based configurable in the Settings dialog.
e1d651b: Cleanup (formatting)
a7e55c6: time limit for constant search
9cce5c7: do not split annotations on multiple lines for params (IDEA-127017)
0aa3890: simplified, added filtering for subsequent parameter pairs: "key, value", "first, second"
d477f91: IDEA-59662: Provide ability to specify right margin (columns) for each file type separately (enabled for PHP)
9b820f7: IDEA-127679 ArrayIndexOutOfBoundsException if no default or focused button is specified
4c997e0: Prevent NPE in AbstractPopup and add corresponding logging
5a49a15: fix inset: VerticalFlowLayout incorrectly uses vertical gap as top inset
ece44da: cleanup
5a46a42: generic debugger settings: step 3 — root settings (Debugger node content)
04518cd: Inspections settings ui: tools not initialized if no necessity to apply inspections filter
da81c5d: ProjectFileIndex#isIgnored renamed to 'isExcluded'
0e67032: Return accidentally removed PyStringLiteralFixer
3089c9c: PY-12877 Complete statement doesn't insert colon for 'with' statement
1c70a28: IG inline fixes: use inline method processor; if simple getter is inlined in simple expr statement - it should be deleted (IDEA-127135)
8835827: logging for (IDEA-127288)
b585c95: inline method: check unique names for this variable in initial class scope (IDEA-127411)
ccc235e: tests: show warning when only ignored tests were detected (IDEA-127660)
ad42de6: vcs: reformat and cleanup
e9fe369: fixed starting jps-builders tests
9d121c2: fix disposeUIResources if merged
2e780d7: turn off contract inference for overrideable methods: fix logic
1bf7333: don't measure MinusculeMatcher performance
2eae3c8: contract inference: take explicit parameter notnull into account (IDEA-127667)
8851417: non-code gutter: get annotations for bytecode psi when highlighting library source
6608b52: generic debugger settings: step 2 — stepping — transform Groovy, all Stepping configurable on one page
e46729f: cleanup
dd0fd0f: ContainerUtil notNullize/nullize
3d30b27: extract EMPTY_SETTINGS_PANEL_PROVIDER
efe0d5a: Inspections settings ui: filter actions in toolbar added
006705c: DefaultVcsRootPolicy refactoring: 'isIgnored' check moved from 'matchesDefaultMapping' implementations to 'getMappingFor'
363b2cd: simplification
12a3d4f: platform: minor optimization (do not rename/delete directory recursively)
9a80d33: vfs: on create/delete notifications mark a child dirty as well
74d6cb2: diff: use separate action for 'roolback' for file (VCS action) and 'roolback' for local changes (LST action)
8b69a0f: diff: fix access to the invalid ranges
1f4de9d: diff: diff from LineStatusTracker
950a80a: IDEA-110611 diff: Ability to revert lines in selection
b6cd05a: diff: small fixes in LineStatusTracker
db873ee: Use Guava's immutable lists instead of static initializers in PySmartEnterProcessor
b1b8116: PY-9176 Converting % to format() doesn't handle multiline string literals
3d377d9: trigger automake for deleted files
1101b55: Merge remote-tracking branch 'origin/master'
3bf8f90: fix paint track
1e1a9c7: IDEA-61550 respect EditorFilter
831cecb: Merge remote-tracking branch 'origin/master'
28dd985: make editor vertical scrollbar bg the same as editor bg on Mac
c3a963a: fix preferred height for JComboBox too
da026c5: override ControlDoubleClick+Left/Right, make new bindings switchable
222d8ef: bind 'clone caret' actions to ControlDoubleClick+Up/Down
41034f6: DirectoryInfo: extracted sub-class
693b2a2: DirectoryIndexTest: renames
a06bd87: Inspections settings ui: added mask and fixed height of severity icon
73dfd94: parameter name folding reducing: - do not inline if parameter length is less than 3 - do not inline if two subsequent parameters has interval names (beginIndex, endIndex; startId, endId; from, to...)
90d58be: DirectoryIndex: removed unnecessary isProjectExcludeRoot method
359bc94: DirectoryInfo simplification: use booleans instead of bit flags because now we have no so many instance of this class (CR-IC-5807)
df51a0d: Inspections settings ui: severity colors in tree showed only for enabled inspections
931d600: Inspections ui: expand/collapse tree fixed
c0d2162: EA-58395 - NPE: PerlMatcher.matches
d203d8f: bytecode-inferred annotations are only available for compiled elements (IDEA-127608)
5c1256f: Merge remote-tracking branch 'origin/master'
bacc3c2: lambda -> method refs: collapse when acceptable method without supers found
3bed881: Merge remote-tracking branch 'origin/master'
dd4ddfb: method ref -> lambda: names based on resolved method (IDEA-126977)
4b7122b: shorten FQNs when insert casts (IDEA-127573)
91f505e: create static method from usage in interface (IDEA-127563)
124d774: generic debugger settings: step 2 — stepping
72a3ffa: Cleanup (formatting)
7016425: Cleanup (two utility classes merged)
ff16ce7: update about graphics
02939b8: add problems listener to update counter
22a3964: +getList() to access configurables list
79a933c: update problems counter for Darcula
b198c6a: add Problems counter
67cbc12: allow to override layouting algorithm
43ab48e: + getErrorsCount()
72925df: pointless expressions: do not simplify a - b - b to a - 0
824f432: workaround for <p/> inside <pre> in javadoc preview in JEditorPane (IDEA-127430)
ae4f3d9: unwrap: adjust caret before RParenth (IDEA-127580)
a46f4ce: anonym -> lambda: disable in case when inferred type differs from type of anonymous class and parameter of param type is used inside (IDEA-127603); BTW param.toString would be also rejected
89dc528: new inference: no substitution during most specific inference by means of new spec (IDEA-127584)
fb786d3: SSR: improve annotation name value pair matching
20cb223: SSR: renaming
6af6e5d: SSR: Nullable annotations
912772c: SSR: remove unnecessary field
1017a77: SSR: remove a bit of dead code
04d10f8: IDEA-127337 Popups in editor sometimes aren't shown on first invocation
f18c04d: jps plugins: added extension to allow plugins contribute to JPS model even if they don't contribute to external build
5c81146: generic debugger settings: step 1 — data views — merge settings (generic and custom) into one page
4a15d1b: add copyright
27fddc2: generic debugger settings: step 1 — data views
e6d092e: cleanup
343e145: cleanup
90cc9e3: cleanup
30c7264: cleanup
31d8039: cleanup
322e539: IDEA-127077 (highlighting test)
4d7f4ab: move PropertiesComponentImpl to core-impl
2495d31: runtime-opcodes support
f3ffe1a: java-analysis-impl doesn't depend on platform-api
68de28e: use JBColor
fd0bb1e: IDEA-127620 - Clouds: move frameworks to the top level in new module/project wizard
0cf69f8: bytecode analysis: resolve only wrt PsiClassOwner (IDEA-127608)
9ca2a55: reverted e442926 because there is no thread visibility problem right now (per discussion with Roman.Shevchenko)
8e74038: additional tracing code for difference of this session and previous session of particular content calculation
bcd970c: Nullable / NotNull
62a0104: revert: Ctrl-N doesn't show after delete; no input field for Ctrl-N etc
9b4912c: lambda: do not start inference from void type (IDEA-127596)
6de6ee1: delegate methods: do not suggest to delegate when delegate would override final (IDEA-127605)
9ff4e24: plugins update dialog layout (IDEA-127462)
f03ca65: create class: filter out generated roots (IDEA-127562)
455fc35: move destination: fix laziness
d93d0ab: Workaround for another JDK bug in cached popups.
a4cdd68: update grails project structure: remove proper instance of ContentEntry (IDEA-120622) using urls since ContentEntry uses default equals/hashcode implementation
14d57e4: Cleanup (formatting)
e35d9d8: IDEA-127077 (incorrect parsing of type annotations)
653ccf4: move to the right module
0e44e1c: Merge remote-tracking branch 'origin/master'
afc422e: fix errors foreground for Darcula
f38dd67: Merge remote-tracking branch 'origin/master'
bbb42a45d: IDEA-126982 (Add an inspection to flag a nonfinal class that only has private constructors)
c54ff24: fix typos
8ae3019: [git tests] assume git version is supported
1e79d7b: [log] Simplify & fix refs comparison API & implementation
dd0a29c: [git] fix refs comparator for the case when refs have equal names
db96ca3: [git] remove trivial test setup
7f6d1ce: [git] rename test class to match the production class name
d94a9bf: simplify:inline variale
a2d93f2: IDEA-126389 FromString ClosureParams Hint should use method.getTypeParametersList() as a context for creating type because method does not process its type parameters in processDeclarations(). Use method as a context if it does not contain type parameter list
a2221d5: IDEA-126334 Correct declaration scope for light parameters and getReturnTypeElementGroovy for light methods
5ccb872: IDEA-126389 FromString ClosureParams Hint should use method.getTypeParametersList() as a context for creating type because method does not process its type parameters in processDeclarations()
b43f06c: make another IG test light
09fafa8: IDEA-127263 ("Conditional Expression With Identical Branches" deleting non-identical branches.)
feeb54a: SSR: remove unused method
d5ce2c1: SSR: match name value pairs when value is not specified
d61fdb0: fix typo
55eb67a: fixing JspAutoImportTest: auto-import classes at statement start
455b352: cleanup. remove unused fields
42c3f0d: change links protocol
05a4407: add hyperlink listener
9906008: Merge remote-tracking branch 'origin/master'
25f1003: stub for ordered scopes problem
7e4a359: Merge branch 'master' of git.labs.intellij.net:idea/community
3559eb7: bundling ecj-4.4 java compiler
0d34ff1: PY-4186 Lettuce BDD tool support (initial commit of lettuce runner)
640c9a6: Merge remote-tracking branch 'origin/master'
e442926: IDEA-117507 NullPointerException on startup of IDEA 13
d2be1f3: disable hard consistency check
5cc3a16: fix mistype in html+ increase space between elements
2dc1b7c: Log popup window size before showing
1a20228: showing build attempt in logs and in title bar only
2df962a: scopes: include module in file pattern as matching would be calculated against it anyway (IDEA-127397)
d60644a: Resize a popup window according to the preferred size of the component instead of its size.
b237157: do not lock UI during plugin download (IDEA-127454)
067ef9e: method refs: check first parameter if it could be a receiver for varargs methods (IDEA-127506)
eff4567: compose error message (IDEA-127534)
3bc9fa3: IDEA-111466 Mac OS X keyboard shortcuts with alt don't work under Oracle Java 7
6bf1409: postfix template fix ternary operator + test
c6ff466: IDEA-111466 Mac OS X keyboard shortcuts with alt don't work under Oracle Java 7
55a8bd4: DebuggerSettings — use PersistentStateComponent instead of deprecated JDOMExternalizable
189f3f5: cleanup
402ccab: DBE-228: column margins
333636c: Replace comma with ampersand
68ec56f: Add groupId for top-level configurable for Junior IDEs.
2ff73e2: don't recreate code fragment psi (IDEA-127039)
5379b77: turn off contract inference for overrideable methods; hopefully, not forever (IDEA-127518)
49a119a: IDEA-121276 Constant conditions: work with arrays
ec7c9a3: JavaConstantExpressionEvaluator: add @Nullable
563ad9e: move DfaValue calculation for expressions to one place
392885a: @NotNull DfaVariableValue.psiVariable
ee1eb2c: store / use buildAttemptInfo taken from file
5de0503: insert delete with renaming logic for Windows into general delete method
0a6a374: Reverted: cast from Short to char is prohibited (1c5a03589e5155633de91051e59255d3ad84414f)
91b8291: IDEA-123863 'Save Project as Template' action leads to deadlock
3aed563: Platform: use presentable names when generate keymaps for help
34f2fb0: grayed call frame view if in library content (the same as in java)
f9982ec: update grails project structure: remove proper instance of ContentEntry (IDEA-120622)
ecbbac9: Merge remote-tracking branch 'origin/master'
9795ec8: extract from internal mode
3b00516: notnull
67b840e: notnull
e4308c8: cleanup
e75bfff: correctly calculate inspection short name in presence of upsource
52d5382: optimisation
1c5a035: cast from Short to char is prohibited
685fdb4: optimisation: getMaybeSuperClassSubstitutor() allows to avoid extra isInheritor() check
60d088f: notnull, cleanup
b5fb827: optimisation
edf6a02: Merge remote-tracking branch 'origin/master'
23586b9: Added coverage.py fixing wrongly optimized imports.
f291cc5: Fixed getting helper.
ecec042: Merge remote-tracking branch 'origin/master'
f997ddb: StringPattern: restore binary compatibility (broken by 64c812e)
145fbae: Merge remote-tracking branch 'origin/master'
642faba: as ignored file set can be quite large, use map without entries for smaller memory footprint (e.g. in snapshot for IDEA-126201 number of ignored files is 126K, size of entries's memory for this number of elements is 2.5M)
067aa19: skip non-java files with given word in literal even before retrieving text / searching the word / finding element at tree (IDEA-126201)
aea7066: allow to pull methods in super interface as default ones when super interface already contains a method with the same signature; remove @Override in such case (IDEA-126988)
7d19e0e: method refs: missed super class substitution (IDEA-126969)
f17f398: 'unmark root' action: allow user to cancel exclusion if excluded folder is selected (IDEA-23537)
d61da37: IDEA-122076, IDEA-122172 Multicaret actions in quick find mode
8b2824d: IDEA-121956 Multiple Carets: Add support for skipping next selection
1ce6009: - catch runtime problems (e.g. IndexOutOfBoundsException from ByteBuffer) when instantiating stub index and retry index creation (IDEA-117596, IDEA-125750), ep2
c455dd4: - catch runtime problems (e.g. IndexOutOfBoundsException from ByteBuffer) when instantiating stub index and retry index creation (IDEA-117596, IDEA-125750) - use deleteWithRenaming to update index version (IDEA-127160)
b0fe937: DirectoryIndex: fixed 'isIgnored' for ignored files
fb4de8d: DirectoryIndex: store info for excluded and ignored directories
df68af8: require that notnull fields be initialized (IDEA-114889)
5a22fdc: IDEA-126660 Incorrect 'condition is always true' for a final field if an overridden method is called from the constructor
a7ec8b7: IDEA-126173 Specify exception type thrown by @NotNull annotations
f4d3703: IDEA-126531 Completion inserts unwanted semicolon
2887069: dfa: take into account known variable values when handling number comparisons (IDEA-126446)
1aa6e7a: add cast to disambiguate method calls when replacing their argument with null (IDEA-126466)
65ccc19: HardcodedContracts: use a utility method
4853112: dfa: we don't replace nontrivial references with constants, so remove the corresponding code and tests
3eea5bc: hardcode Guava Preconditions.checkNotNull contract
6d9cf39: dfa: understand assertThat notnull (IDEA-125977, IDEA-65004)
2a3e157: Merge remote-tracking branch 'origin/master'
f5e85e7: IDEA-126122 (reFS volumes recognized)
be1c5b8: Refactoring: configurable from provider can be null
e568624: Cleanup (file watcher project converted to VS13; generated junk dropped; version info updated)
1e7a9e9: Cleanup (tabs/spaces; formatting)
0262088: cosmetics: add extra offset for project icon and label margins
1695e0c: fix AIOOB in history
2b3dd78: add css styles to Error Pane
ac796ba: wrap errors to html for new project structure dialog
59763f3: Focus logging
3d07af1: SingleInspectionProfilePanel NPE fixed
397e893: IDEA-127478 Tip Of The Day dialog could be shown on incorrect monitor
090b2e5: on case-insensitive file systems perform file name compatibility check using real file names, not the ones taken from dynamically built or stored paths
e77cd8a: imports optimized
444f98b: show tooltip when mouse on "multiscopes" icon in inspections settings ui
b0b0fef: external build: send empty text with statistics message to avoid producing unnecessary output by IDEA Project Runner in TeamCity
970fe60: use ProjectFileIndex (from API) instead of DirectoryIndex (from impl) where possible
d9c45be: libraries scope: use ProjectFileIndex (from API) instead of DirectoryIndex (from impl) where possible
417adc7: notnull
b58808c: js postfix templates -> fix examples and configurable UI
ba3b637: Settings dialog: the project-based configurables are marked with an icon.
6508417: IDEA-127438 Tool windows drag&drop failure under Java 7
ca76151: IDEA-127343 Github: pass ModalityState to PasswordSafe
da5b7e6: use Couple
e98cb25: use Couple
568b881: DBE-228: abbreviate long cell values
abd5dce: notnull
6d84ba6: Add image to visualize the project-based configurable in the Settings dialog.
c32d9db: [vcs] IDEA-107598 Affected paths: consider the whole path when highlighting
f4ee468: Inspections settings ui: new inspections tree (tree replaced by table tree, added icons represent severities)
47de3cf6: LiteralExpressionTokenizer cleanup
120fe08: get rid of group border
1a5cf59: IDEA-127343 Pass ModalityState to PasswordSafe.store/removePassword
0143935: remove trivial javadocs
f3eb2b2: [git] Remove obsolete password aware handler
c4a16ff: Get password called with modality state; unnecessary get password from memory removed for hg auth
bf1f916: restore API for idea-php-symfony2-plugin
c53dc62: check for null
62e5744: unused classes from 'old make' implementation deleted
b8ddfa4: groovy compiler: deleted classes related to old make implementation
42926e1: Fix possible NPE in JiraRepository (EA-58392)
3498760: Introduce live integration tests for Tasks plugin. Add initial version of such tests for Trello
6fd747b: Migrate TrelloRepository to new tasks API
3021914: Remove wrong GSON annotation of 'closed' field in TrelloCard
e2b581a: IDEA-115177 (watcher size check no longer needed)
c9d3e32: vfs: file watcher executable lookup fixed
431f9db: get rid of deprecated code
25c8ccd: EA-58277 (diagnostic)
6037400: don't highlight '*.restartRequired' properties in registry.properties as unused
dfb7e51: registry: use 'restartRequired' property to enforce automatic restart
3305c35: registry: description text corrected
0ffc7d0: CIDR: OC-10422 Current resolve configuration should be preselected in resolve context panel
5f31e2e: show excluded files in Project View, initial (IDEA-23537)
ace6fe4: artifacts: one more unused class from 'old make' implementation deleted
5897a01: deleted old unused deprecated 'build participant' classes
b75d5f2: artifacts: unused classes from 'old make' implementation deleted
3ed24eb: RUBY-15507: we should not wrap configurables in scroll pane
26b2743: ProjectViewPsiTreeChangeListener updates tree more deeply on property changed
77ee53a: Fix again the following changes that were reverted: Fix for nullable composite configurable.
d0cd301: IDEA-126984 Mac OS X: Double line above the editor
c2e6ab0: revert (e3e3224e853ad466cebf13c8b5c7f01d272d8e88 Fix for nullable composite configurable.)
d1ce059: highlight modules
8b6c40d: platform: splash misalignment on Windows fixed
8b35719: devkit: incorrect modification reverted
928feba: JavaHelp 2.0_05
bc13d12: Merge remote-tracking branch 'origin/master'
a9d7da6: added test for default property file in resource bundle editor
160fca0: project: windows update scripts unified
e3e3224: Fix for nullable composite configurable.
a69f781: cleanup
aa16ba2: IDEA-126984 Mac OS X: Double line above the editor
0994232: project: build/update scripts tuned to support home paths with spaces
b9eb20d: EA-57720 - PIEAE: LeafPsiElement.a
1396322: IDEA-127343 Pass ModalityState to PasswordSafe.getPassword
7eb4791: [git] remove obsolete javadoc
4d3abb7: cleanup: remove trivial comments
d8f6d4c: SeverityEditorDialog -- if "mixed" pseudo severity chosen then no default selection
e39d6a2: ScopesAndSeveritiesTable suppress unused declaration inspection
f356442: DBE-321: prevent data tooltip flicker
226e175: Inspection settings UI: multiselection in inspections tree support
68c651e: catch exception when writing content, marking vfs for rebuild (IDEA-125925)
f59b7b3: revert the change that breaks functionality
eac71fe: add tool window id to assertion
349f84a: fix several concurrency issues in Search Everywhere
65824bb: Some configurables requires the dynamic attribute to configure its wrapper properly.
73c7f62: go to action: move Analyze Stacktrace... to the top
5458ff1: IDEA-127232 Fatal Error on startup on Mac Os X
09230b2: IDEA-125671 "Install plugin from disk" action could have different description in Find Action list, because now there are two identical items in the list
35e6c4d: remove suppressions
e83f80f: implicit usage provider for *.description in registry.properties
ddae39a: file path completion fixed
6f1f6cf: Inspection settings UI: creation copy of tool wrapper if new scope inserted
981a3db: remove unused class ColumnInfo$StringColumn
864cd2d: Inspection settings UI: - "ALL" scope moved to last position in table - move scopes in single inspection disabled
4965683: Merge remote-tracking branch 'origin/master'
e488923: cleanup. remove unused methods
d13074e: fix incorrect target element calculation for text editors
e2c4550: typo
219b31c: show display name for certificate configurable
1b4a3c1: larger scope when searching in path / ssr (IDEA-127068)
17a5843: disable plugins if cycle was detected (IDEA-127177)
001a9cf: New inspections settings UI: - nodes for individual scopes deleted from settings tree - added table in right panel to configure scopes&severities - new scope button added to right panel (if only default scope currently available)
de377f8: extract variable missed events (IDEA-127166)
dcb45f2: extract field missed events (IDEA-127167;IDEA-127169)
1d8d358: extract parameter missed after event added (IDEA-127170)
30d945d: local rename events (IDEA-127172; IDEA-127172)
d0b86b6: popup position for injected fragments during inplace refactorings should be calculated over injected editor as caret range marker is against injected document (IDEA-126989)
53de53f: Refactoring of grouping configurables. Use the following registry key to switch on this functionality: ide.file.settings.order.new
088e9a2: Fix tests broken after changes for PY-4073
d5032e5: java: parser changes temporarily reverted
690797e: Using "Batch File Change" events in BuildManager to cancel already running automake session while VCS is updating working tree (IDEA-111673)
4e586b4: replace with foreach
b70bfe3: Platform: do not produce duplicating shortcuts in help keymaps
d65ce21: Merge remote-tracking branch 'origin/master'
6a141b3: [git] rename utility method to a more consistent name
c5129cf: fix layouts: exclude structural search from defaul platform implementations
a9396b6: unused property inspection: search in a narrow scope first
5002c70: contract inference: honor already known parameter values during data flow analysis
704198c: contract inference: exclude contracts not matching the return type
349f900: no ContractInference for compiled elements
f02a497: change foreground and separator color
2e6fb66: move Problems down and change separator
6404449: support "--" separator
aa98745: customize separator
2e376b2: [git] IDEA-126472 IDEA-126473 Notify about external changes modifying the working tree
1a37016: [git] better diagnostics output format
f9a615b: [git] remove unused method
8b2eae6: [git] Remove duplicated code
5dd8682: Merge remote-tracking branch 'origin/master'
91b1e1b: Fixed wrong cast.
44ec294: annotate getPattern / getOptimizedIndexingPattern with @Nullable return info
50d32b8: SSR fix replace test
6521af0: IDEA-127077 (incorrect parsing of type annotations)
5b72fac: Cleanup (warning)
4fd07a5: License dialog: License key: provide precise diagnostic for rejected key After-review
6cb2eab: Remove deprecated Remote Interpreters API.
7d1e1bd: do not propagate exceptions in case of broken bytecode
980b837: fix test
e099a14: gant-based builds: added one more jar required for Kotlin compiler
f36393b: go to action: use gray instead of italic for shortcuts rendering
1c43998: gant-based builds: removed incorrect classpath entry for Kotlin compiler
ffb8b5f: gant-based builds: added jars required for Kotlin compiler to classpath
47eec66: java inheritor cache: use weak map to avoid holding PSI for unnecessary long time
1a78fe7: goto action: restore enabled action preference
8343e8e: goto action: rewrite using a more suitable API, prefer exact matches, fix search everywhere
a24b862: IDEA-127189 Select next/all ocurrence fails with braces
304a69b: IDEA-103025, EA-53787 fix handling of special fonts in soft wrap model
dd4b761: trait field searching test fixed
7c04faa: Merge remote-tracking branch 'origin/master'
bad2fa8: Fixing env tests
dd23104: memory hogging on javaInjections.xml editing
1bbb89c: go to action: proper text attributes for shortcuts
0057fe3: go to action: don't show duplicate group for intention actions
482084c: go to action: missing bg for option descriptors
590ed4c: disable create instance field inside interface (IDEA-127211)
65d11dd: disable generate test method actions outside class (IDEA-127246)
f69f9fd: leave static modifiers in interface methods (IDEA-127255)
42f50f6: sort items in goto action by matching degree (IDEA-112613)
45915b2: show colored matched fragments in goto action (IDEA-112613)
34b51a1: show progress when status bar is switched off (registry: ide.show.progress.without.status.bar)
cdbb650: null check
d5dada4: disposed check
64c812e: StringPattern: cleanup, add StringPatternTest
7fa1b28: remove unnecessary field and getter from SSR ReplaceCommand
472bf3f: java: decompiler fixes (IDEA-127229 et al.)
7ada79c: AnnotationUtil.isInferredAnnotation
c0d76a2: parameter info: don't retrieve inferred annotations in dumb mode
4df5903: IDEA-127127 charset name completion: provide completion for more APIs
b6ea24b: don't duplicate annotations in parameter info
3f7935a: dfa: turn on contract inference from source
b6b76b7: IDEA-127212 Live templates "Use static import if possible" fails sometimes
f372b95: corrected 'isCommunity' check
08b3c7f: disable table speed search for empty tables
51292ab: Merge branch 'svn_18_3'
511f071: IDEA-126911 Do not call "VirtualFile.getFileType()" for directories (while checking if annotate action is enabled)
93e45b9: PY-4073 Add completion for special function attributes
0a5bfb0: Merge remote-tracking branch 'origin/master'
0fcfb36: Merge remote-tracking branch 'origin/master'
86b7bf5: Fix copying .egg files from remote host to libraries folder (PY-13044).
912c0d7: html editor as a error pane
55b4064: do not modify profile file to write default
102bcb2: structural search bundles fixed
bb0b50c: Merge remote-tracking branch 'origin/master'
5253ba2: type migration: make part of platform
3ab8864: Error pane initial
581cf80: structural search: groovy should work in community
76726e8: disposed check
be28458: css synchronizer — support of manual update
7b29836: structural search: make part of platform: enable in community
a5061f3: fix module locations
a3626d6: Merge remote-tracking branch 'origin/master'
695682b: structural search: make part of platform
50e27ec: enable inline redundant local vars in batch mode (IDEA-126957)
49b9f30: EA-58014 (diagnostic)
9d32e98: added incompatible plugin version org.intellij.clojure / 0.2.1.178
c27d829: - properly retrieve set of keys for unsaved documents for their first change - (in extra sanity checks mode) added PHM that stores previous content indexing info to facilitate discovery / fixing assertions about different indexing behavior
0896a25: avoid accessing invalid buffer
159f3c5: Merge remote-tracking branch 'origin/master'
4cc0d5e: IDEA-127183 Windows: Look for 1.8 JVM registry entry by default
f41f9c3: Debugger reader performance fixed, now uses BaseOutpuReader (PY-11393).
cb2159e: Switch on new Alt processing under Windows L&F.
ef8fbc4: notnull
e1445e6: Gradle: IDEA-127217 Tests with spaces aren't wrapped with " " when running specific test as Gradle test
cf1f8c8: fix tests: improved 'isInCommunity' check
bcb38e5: pep8.py updated to version 1.5.7
0970549: Coverage.py updated to version 3.7.1
90f034d: Merge remote-tracking branch 'origin/master'
6ec557a: Try even more to connect.
69a2a4c: path completion: rendering fixed for several matched context helpers
21efa65: cleanup
7494ef6: support for multiple groups in idea.test.group property
00847ed: No need to register MessageBusConnection disposal: it is done automatically
0fb6eab: Suppress "unused" constructor: initialized as a Service
2b6b549: Fix already disposed EA-58257
fc46e99: look for JVM 1.8 first
b7cea46: added caching + minor size opts
63453c3: fix NPE in SSR ReplaceOptions
941c20b: stop usage search on target invalidation (EA-54329)
d75a12f: invalid type diagnostics (EA-58069)
121084a: remove specific Lookup check in intentions pass, daemon is disabled during lookup anyway (EA-58246)
71fd543: WEB-9954 move and rename confusing "Live Edit menu item
5d745b1: check for null for content hashes flush
7073116: fix ConcurrentMapsTest (by Roman)
6d6d15a: IDEA-122254 ("Use static import if possible" checkbox for the Structural Replace dialog)
4febd80: intention should insert static import when replacing assertNull() with assertEquals()
4dd5b62: insert static import when replacing assertEquals() with intention
591d723: insert static import when flipping assert literal outside of test method
90f2ef5: use static import if it is already present for several intentions and inspections
dd2d141: Merge remote-tracking branch 'origin/master'
ae714aa: Win tests should be more stable now
3b3fee8: clear zip file caches on low memory
c81ebf5: Refactoring: create instances of IdeConfigurablesGroup and ProjectConfigurablesGroup in one place.
b83f901: Merge remote-tracking branch 'origin/master'
9c13559: Comment.
ec56a95: skip leading .* in reg exp used for todo pattern when building index: it makes sense for todo highlighting but increases matching due to backtracking enormously (IDEA-74104)
d83e334: IDEA-126836 Formatting adds extra blank line after class header if it ends with comment
6d68662: use 1px separators
e360f62: add utility method to configure 1px separator
23ba8f8: use encoding info when building hash for contents + rebuild content dependent indices upon encoding change
7b8cc6c: Fixed according to CR-IC-5706
93d37fa: don't auto-import classes where class references are prohibited syntactically (IDEA-127190)
f87e56d: bytecode analysis: a comment about lite control flow analysis
90addcd: bytecode analysis: lazy resultOrigins analysis
a7c1c1a: bytecode analysis: lite control flow analysis
8211223: bytecode analysis: no analysis at all if nothing is inferrable
60a6acf: moved method from moduleutils to modulerootmodificationutils
3173594: jetbrains.dic + "unmerged"
c95ca18: support prefix expressions in quick evaluate
8d71285: cleanup
e418bb2: use UIUtil.getSidePanelColor()
bef8a7f: remove border for content component
1835567: + side panel color
105b9a1: @Nullable
ef2875d: IDEA-126927 - CloudFoundry: if the same artifact is deployed to few clouds, one can view the state of the single one only
7c415ca: IDEA-127069 help button added to the Mercurial Update Project dialog
0e12675: Revert "StringUtil: Added wordWrapString() method"
5af9e9f: separator titles for Darcula
b044185: better IDE responsiveness during bytecode indexing
15ed602: svn: Fixed converting SVNStatusType to StatusType - explicitly check values that have non-unique status type names
43b6e08: BytecodeAnalysisIntegrationTest: a more profound test data refresh
525a896: Merge branch 'master' of git+ssh://git.labs.intellij.net/idea/community
fe15e86: bytecode analysis: index version increment
0d24afe: disabled batch evaluator by default, overhead is too high. Enable if needed using registry key debugger.batch.evaluation
0069112: added nontull
062254d: svn: Refactored SvnStatusHandler - moved status type parsing to StatusType class
a884c1b: ignore inferred annotations when checking overriding parameters nullability
b6b6ea1: bytecode analysis: lite persistent hashCode for Configuration
1f05788: bytecode analysis: skipping frames and debug instructions
aa3b189: svn: Refactored StatusType - removed deprecated STATUS_MERGED value
f2d0988: do not capitalize titles
5ba1390: extract CachingEnumerator from PersistentStringEnumerator
1cd8961: IDEA-126973 IDE does not see the use of string resources in libraries
bb0b856: @NotNull
b700084: nullable/notnull/contract intentions available on library sources in Groovy
18afce3: external/inferred annotation gutter markers in Groovy
18dcabb: [vcs] assert annotationProvider != null (otherwise the action shouldn't be enabled)
ff89749: [vcs] Handle all exceptions during annotation similarly. EA-58021
45cd16b: svn: Refactored SvnVcs.svnStatusIs() - method moved to Status class
37423bb: StringUtil: Added wordWrapString() method
228a7f0: IDEA-127144 (No word wrap or horizontal scrolling for ignore list in Proxy dialog)
a570ab1: SSR: remove unused code from replace options
e19e11f: SSR replace handler cleanup
ab42788: IDEA-125021 Improve multi-caret copy-paste logic
8662259: IDEA-127125 Throwable at com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter.reload
7c0fcae: fixed Breakpoints tests
be3789a: do not calculate source position twice
a22e159: Disable css emmet fuzzy search by default
687fa76: no need for findClass if we have object already
86d795a: ElementPatternCondition: less memory
7801b62: ObjectPattern.oneOf(T...): less memory for single value
394b9f4: GPUB: keyword completion on non-trivial offsets
d9d79ac: resource bundle editor property rename fixed: no property with old invalid name creation after rename
233bdf9: svn: Replaced SVNStatusType (from SVNKit) with custom StatusType enum
5803898: action popups are broken if new project settings dialog option is on
1b4fef2: svn: Replaced SVNEventAction (from SVNKit) with custom EventAction enum
cbcc65f: more robust lookup start offset invalidation tracking (EA-51961 etc)
4703c93: IDEA-75885 Fast invoking shortcut with Alt brings focus to main menu [Windows] IDEA-87408 Hold <ALT> button and select multiple lines vertically does not work as desired
19eb679: Add diagnostic info
40acdf7: License dialog: provide precise diagnostic for rejected key (IDEA-122894, IDEA-125916, IDEA-125917, IDEA-125921)
e28ee1b: display inferred annotations in italic
fe21a09: IDEA-81781 Unintuitive 'Project Files Changed' message box
78b8b2c: Fix emmet tests
b363ad7: notnull
ac8bf17: added comment
d61930f: revert 43b0575239264eef2d4a5299b48f3d2119d8d380
3674c5e: PY-13140 Fix several minor issues found during code review
99058ce: TableView: update only visible column sizes
4a8c1f7: fix BytecodeAnalysisTest?
6563b56: fix BytecodeAnalysisTest?
f6c970f: IDEA-126896 Gradle: Grails integration buildPlugins issues
1ee9d0e: ModuleWithDependentsScope, another minor post-review cleanup
1478698: Fast groovy tests: exclude external system integration tests from FastGroovyTestSuite (e.g. GrailsGradleImportingTest)
43b0575: IDEA-126836 Formatting adds extra blank line after class header if it ends with comment
abcc172: IDEA-127062 Bug when closing splitted editors
ee204e9: IDEA-127045 Unexpected 'step over' behaviour in debugger
a2fb7f6: DBE-60 Windows Authentication for SQL Server
79fc7d2: processing vcs changed text only if we are not processing selected one
0f34d80: Add diagnostic info
f506a15: PY-13140 Underscored names are completed inside import statement
e0ddef7: PY-13140 Top-level module names starting with underscore are not imported via wildcard import
b6eee00: fix PushedFilePropertiesUpdater registration
b9e4b83: BytecodeAnalysisIndex is only in idea
4dfa351: BytecodeAnalysisConverter is only in idea
cb79e2e: if editor has selection, reformat only selected lines, when reformat dialog is not shown. Do not take into account processChangedText flag
76ed9f7: Merge branch 'master' of git.labs.intellij.net:idea/community
c3c78c5: IDEA-93948 Code Style->XML->Other: no preview text for CDATA
d08111d: XML Colors settings: fix description text (missing line breaks)
34dcb63: SSR: remove unnecessary class
472994b: remove some dead code from SSR search dialog
fca4af3: more efficient shortening of class references in SSR
560fb1c: fix not-null assertion
e13715a: fix NPE
9665337: javascript-psi-impl shouldn't depend on lang-impl
2a91d19: fixed XBreakpointManager.testSerialize
f698722: ModuleWithDependentsScope minor post-review cleanup
9d399a3: WEB-12568 Dart autocomplete deletes closing bracket
95673d1: don't use getPackage to check if PSI directory is in source (SCL-7243)
fbbb221: bytecode analysis: copying to prevent indices/enumerators issues
831ab18: bytecode analysis: compound keys - maximizing shared stuff
0ddd915: enable inference bytecode indexing only in internal & test mode
ee888c2: fix AnnotateMethodTest
e0861b8: bytecode analysis: leaking parameters analysis
31d93dc: ProjectBytecodeAnalysis -> service
fbb2825: ignore inferred annotation in @NotNull propagation to overridden methods
1b75318: adapt testdata to inferred jdk annotations
f350b3d: hold inferred @NotNull annotation on a hold reference to avoid UsageViewTest failures
0e53e62: bytecode analysis: assume that errors are due to incorrect bytecode
affe6e0: log bytecode analysis related information with debug level
a11744c: adapt dfa testAlexBug to jdk notnull inference
f98006e: ProjectBytecodeAnalysis: use tree hierarchy to find parameter's declaring method
bfbc9e0: limiting number of elementary steps of bytecode analysis to 30000
8eee4f3: import fix
261018e: logging AnalyzerException
ce0e838: bytecode analysis: increase IDE responsiveness during large class file analysis (e.g. groovy's ArrayUtil)
3357716: bytecode analysis: use separate keys for each file to avoid O(n^2) index removeAssociatedValue
7fc5d9e: make BytecodeAnalysisTest more classloader-agnostic
bd2ebf1: cache enumerator data, remove unnecessary enumerator queries
14ed555: do postponed formatting in JavaChainLookupElement (EA-56040)
ce64501: no contract annotation if method is already @NotNull
1754869: findInferredAnnotations returns @NotNull array
3b75ca1: refresh annotations dir during setUp
6dd49cb: update annotations after mockJDK update
c5f3f23: hashCode via ordinal, copying equations into solver
90e6e0d: myAnnotations are volatile
08811b0: more logging
1e54e26: simple smoke test that inferred annotations are visible to codeInspection
0db25e9: arrays and primitive collections
658b710: aligned with faba
b993ee5: NPE fix (possible dead bytecode instructions)
8de3dfd: using TIntObjectHashMap in analysis (instead of wrappers)
0ea01ef: minimal structures for inferred annotations in memory
861fbc7: reload inferred annotations on root change
92e2f07: checking enumerators during converter initialization
338481d: Bytecode analysis tests
75eb3cb: straightforward solver
7056873: rigid infinum
6220d1a: porting faba features
ecce111: Contract clauses normalization (sorted)
cd2946b: Tuning null -> ... analysis
82e7fd8: Parameters: CNPE join CYCLE = CYCLE
ade7530: stable calls (ported from faba)
657b7af: starting integrations tests
70cc7af: qname may be null (anonymous classes)
f2e3270: smart propagation (resultOrigins analysis)
05486ff: separate indices
eb58968: a proper disposal of app component in the test
1901b35: this is NotNullValue
f8a153e: equals/hashCode for index values
6dcf377: comments + refactoring
383752b: compound indices in action
ff84337: initial implementation for compound keys
d905c85: towards compound keys
68ef299: Refactoring: moving class processing into DataIndexer
c8e0bef: towards compound ids
bacb5f5: extracting converter
655e0ba: poc implementation via indices
eac23b5: indexing
dabdf64: towards indexing
10fda95: emulating touch via special value
e1fce3d: No need for components (with touched flag) in raw equations
44ea307: testing contract annotations
a8717a4: testing hand-coded classes
fb6ce34: Start test for BytecodeAnalysis
8ec604c: using (naively yet) solutions of IntIdSolver in Idea
fc86f2b: full cycle of solving (not tested yet)
1d2c7d5: in progress: internalizing equations
c31fd4a: No need for signatures
41d2da1: poc usage of enumerators
073012d: inferred annotations in gutter
8c57905: Draft implementation of inferring Nullity and Contract annotations
dca785b: stub for inferred annotations
d83c897: @notnull
aeb0b27: allow / skip build attempt number e.g. IU-135.455.10 (IDEA-122579)
604ec3a: Add workaround for CertificateManager *service* to work with old task connectors
b96c582: Rename classes related to legacy JIRA interface to not cause confusion anymore
165465c: IDEA-123972 Display JIRA version under JQL editor field in settings
6708af2: Use simpler test API for performing actions
eb009fc: introduced expandNodesOnLoad in XDebuggerTree
1c3cefb: nice diagnostics for failed assertion about same indexing result from the same content
6b75304: IDEA-127042 Create tip of the day for quick evaluate
b8d6082: add to watches with control+enter from evaluate dialog
86d958c: for nonntrivial file systems (e.g. in tests): build todo data flag should have the same value as todoindex.isAcceptable (cherry picked from commit 5213e01)
962d04b: svn: Refactored SyntheticWorker - reused default DirectoryEntry comparison for sorting
99e9710: IDEA-107376 Goto next / previous splitter not working when show tabs are set to none (Carlos Ortiz)
0ce61f1: notnull
a0f6b0a: svn: Refactored "checking node kind" logic - use utility methods (instead of direct comparison with NodeKind enum values)
2b5e1c1: update breakpoints and execution line highlighters on color scheme change
69be4e2: svn: Refactored RepositoryBrowserDialog - removed duplication and simplified "set action enabled" logic
e438d20: Merge remote-tracking branch 'origin/master'
1cc9650: IDEA-122894 License dialog: License key: provide precise diagnostic for rejected key (cherry picked from commit ff0eada) (cherry picked from commit 29c98ba)
9241cae: Merge remote-tracking branch 'origin/master'
7d896bb: provide completion variants for Charset.forName() in Groovy (IDEA-126595)
e62b6df: IDEA-126973 IDE does not see the use of string resources in libraries
02e5ede: updated javadoc for exit and restart actions
e1d0804: WEB-12677 Emmet: do not expand too long abbreviations
6046586: Emmet: preview editor should have maximum width if softwraps are enabled
be20e87: DOC-3246 IDEA: Update master password dialog panels help
ca5260d: IDEA-79083 Popup lists are sometimes not clickable
8a05f8e: Merge remote-tracking branch 'origin/master'
be76dc1: highlight matches in plugins table
fcd53e5: save search query as a client property
fd6b2c9: add a client property key for search query
febbd75: fix inspection setting
e119956: svn: Simplified node kind values parsing - parse right to NodeKind enum (using jaxb annotations)
6979d52: do not let transient iterator escape as it references nonsharable thread locals
746de0c: diff: fix example in diff editor color settings
3eebc8b: show different icons for muted enabled/disabled breakpoints
1b1a23a: IDEA-120465 Code completion: no Autopopup code completion and inserting pair brackets if Column selection mode is on
9dde9e3: svn: Replaced SVNNodeKind (from SVNKit) with custom NodeKind enum
ed3bf70: split 'force' flag into two different ones: 'force' and 'exitConfirmed', so that now 'force==true' guarantees application shutdown; (fixes also IDEA-127009)
756bc03: performance optimization (following IDEA-CR-337)
7a94893: change storage format for smart backspace configuration (following review CR-IC-5570)
8feafdf: Updated tests due to changes in interface of CommentByLineCommentAction
5d8aeec: IDEA-127074 Executing gradle script fails with error '.../build.gradle' is not a directory
546e0ba: IDEA-88643 delete item from list of breakpoints by pressing delete key: selection should not return to position 1.
4de85a6: IDEA-122513 Can't expand single character property in Spring config
2595051: fix blinking test
51305d0: fix test (following IDEA-124928)
51c9f4a: IDEA-121060 Multiple carets: comment/uncomment works incorrectly
0ddcf6e: IDEA-121060 Multiple carets: comment/uncomment works incorrectly
d51dd50: IDEA-121060 Multiple carets: comment/uncomment works incorrectly
9307f63: svn: Save selected depth for update/integrate operations in configuration
0e44683: svn: Refactored DepthCombo - use custom renderer to display presentable depth labels (instead of separate DepthWithName class)
c20bfe7: svn: Replaced SVNDepth (from SVNKit) with custom Depth enum
0e8564a: Merge remote-tracking branch 'origin/master'
c3e78ad: Add read action.
54474d0: TestDataGuessByExistingFilesUtil: prevent thread starvation and use processor to avoid several huge String collections in the memory (IDEA-127038)
02c1b76: svn: Utilized CommitInfo when parsing "svn list" output and for DirectoryEntry implementation
ac00218: svn: Unified "lock" information parsing for info, status and browse clients (utilized Lock class for all cases)
3ea1e9e: Merge remote-tracking branch 'origin/master'
61c9bc4: highlight matches in selected search result
e3fd6eb: don't add a newline when completing with smart enter shortcut (IDEA-126726)
4029019: EA-57959 - ISE: DomInvocationHandler.getAttributeChild diagnostics
e3c7a70: don't require PerformanceWatcher unresponsiveness threshold to be divisible by unresponsiveness interval (IDEA-127038)
02b931c: diff: fix NPE
dc4f600: Merge remote-tracking branch 'origin/master'
bcc88e8: svn: Replaced SVNLock (from SVNKit) with custom Lock class
f21bc13: svn: Replaced SVNDirEntry (from SVNKit) with custom DirectoryEntry class
8434e55: svn: Fixed NPE in SvnKitBrowseClient (SVNLogClient.doList() logic) - use SVNRevision.UNDEFINED if null is passed
e6f257f: Added multi-associations in win (un)installer with the controls on the option page.
6eb7f53: svn: Replaced ISVNAnnotateHandler (from SVNKit) with custom AnnotationConsumer class
535cb8c: svn: Refactored SvnRemoteFileAnnotation - removed unused fields
56cd6a8: svn: Refactored BaseSvnFileAnnotation - utilized CommitInfo for implementation logic
6916bac: svn: Refactored BaseSvnFileAnnotation - code simplified, duplication and unused code removed, warnings fixed
79f167e: svn: Utilized CommitInfo when parsing "svn annotate" output
5585db2: svn: Replaced SVNCommitInfo (from SVNKit) with custom CommitInfo class
3b0971e: svn: Replaced SVNDiffOptions (from SVNKit) with custom DiffOptions class
58ab1b9: svn: Replaced SVNConflictVersion (from SVNKit) with custom ConflictVersion class
d287078: svn: Fixed "memory leak" error (on application close) after viewing merge source hierarchy details - correctly register dialogs for disposal
ef3bc87: svn: Refactored TreeStructureNode - made not to be generic, renamed
ff2698d: svn: Unified LogEntry and LogEntryPath with corresponding similar classes used for svn executable xml output parsing
00224be: svn: Replaced SVNLogEntryPath (from SVNKit) with custom LogEntryPath class
6a330b5: svn: Replaced SVNLogEntry (from SVNKit) with custom LogEntry class
615f0cb: svn: Refactored parsing of child xml elements collections - use @XmlElementWrapper (instead of intermediate classes)
5ac00c0: svn: Refactored SvnUtil - moved "getPathForProgress" to ProgressEvent
cef6176: svn: Refactored ProgressTracker - removed unused method parameters, make inherit ThrowableConsumer
17cbb94: svn: Replaced SVNEvent (from SVNKit) with custom ProgressEvent class
57e1c41: svn: Preserve several more used fields when converting from SVNStatus to Status
37798c0: svn: Allow @Nullable values when converting SVNKit types to corresponding svn4idea types
ea32a54: IDEA-125627 Added new conflict reason values available in svn 1.8
099870d: svn: Replaced SVNConflictAction, SVNConflictReason, SVNOperation (from SVNKit) with corresponding custom enums
6dc734f: svn: Replaced SVNTreeConflictDescription (from SVNKit) with custom TreeConflictDescription class
57db2a5: svn: Replaced SVNStatus (from SVNKit) with custom Status class
6730240: svn: Replaced SVNInfo (from SVNKit) with custom Info class
b527300: svn: Removed unnecessary IdeaSVNInfo - directly use SVNInfo instead
46fdb20: svn: Refactored UpdateClient - make methods throw SvnBindException (instead of SVNException from SVNKit)
9ccd315: svn: Refactored StatusClient - make methods throw SvnBindException (instead of SVNException from SVNKit)
bb3582d: svn: Refactored RepeatSvnActionThroughBusy - make methods throw VcsException (instead of SVNException from SVNKit)
33b3795: svn: Refactored InfoClient - make methods throw SvnBindException (instead of SVNException from SVNKit)
ab74ce3: svn: Refactored CmdInfoClient - removed duplication, methods extracted/inlined, @NotNull
29f24cf: svn: Refactored CmdInfoClient - inlined "doInfo" methods, removed duplication
17d79f7: svn: Refactored UpdateClient - removed unnecessary methods
19cc497: svn: Refactored StatusClient - removed unnecessary "doStatus" overload method (call replaced with more general overload)
faa09ef: svn: Refactored StatusClient - removed unnecessary methods
b45cfaa: svn: Refactored StatusClient - removed unnecessary "doStatus(File path, boolean remote, boolean collectParentExternals)" overload method (as always invoked with collectParentExternals = false)
ae24d84: svn: Refactored InfoClient - removed unnecessary methods
2e97768: svn: Added some todo items for working copies of 1.8 and greater formats (cleanup, nested working copies file status refresh)
7874cb3: svn: Refactored SvnCheckoutProvider - removed unused parameters, @NotNull
22b2900: svn: Refactored SvnCheckinHandlerFactory - code simplifications, warnings fixes
1ce0847: IDEA-118540 Refactored "committing changes to same repository but from working copies of different formats" detection logic, updated confirmation message
491ac7d: svn: Updated working copy format checks to be "weaker" - to correctly support working copies with greater than svn 1.8 formats
Change-Id: I25f808eb8c86e4bd21610b40ab6b6df310d8e636
diff --git a/python/helpers/coverage/__init__.py b/python/helpers/coverage/__init__.py
index d8dbc0f..193b7a1 100644
--- a/python/helpers/coverage/__init__.py
+++ b/python/helpers/coverage/__init__.py
@@ -5,19 +5,13 @@
"""
-__version__ = "3.5" # see detailed history in CHANGES.txt
-
-__url__ = "http://nedbatchelder.com/code/coverage"
-if max(__version__).isalpha():
- # For pre-releases, use a version-specific URL.
- __url__ += "/" + __version__
+from coverage.version import __version__, __url__
from coverage.control import coverage, process_startup
from coverage.data import CoverageData
from coverage.cmdline import main, CoverageScript
from coverage.misc import CoverageException
-
# Module-level functions. The original API to this module was based on
# functions defined directly in the module, with a singleton of the coverage()
# class. That design hampered programmability, so the current api uses
@@ -36,12 +30,34 @@
called.
"""
+ # Disable pylint msg W0612, because a bunch of variables look unused, but
+ # they're accessed via locals().
+ # pylint: disable=W0612
+
def wrapper(*args, **kwargs):
"""Singleton wrapper around a coverage method."""
global _the_coverage
if not _the_coverage:
_the_coverage = coverage(auto_data=True)
return getattr(_the_coverage, name)(*args, **kwargs)
+
+ import inspect
+ meth = getattr(coverage, name)
+ args, varargs, kw, defaults = inspect.getargspec(meth)
+ argspec = inspect.formatargspec(args[1:], varargs, kw, defaults)
+ docstring = meth.__doc__
+ wrapper.__doc__ = ("""\
+ A first-use-singleton wrapper around coverage.%(name)s.
+
+ This wrapper is provided for backward compatibility with legacy code.
+ New code should use coverage.%(name)s directly.
+
+ %(name)s%(argspec)s:
+
+ %(docstring)s
+ """ % locals()
+ )
+
return wrapper
@@ -57,10 +73,26 @@
annotate = _singleton_method('annotate')
+# On Windows, we encode and decode deep enough that something goes wrong and
+# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
+# Adding a reference here prevents it from being unloaded. Yuk.
+import encodings.utf_8
+
+# Because of the "from coverage.control import fooey" lines at the top of the
+# file, there's an entry for coverage.coverage in sys.modules, mapped to None.
+# This makes some inspection tools (like pydoc) unable to find the class
+# coverage.coverage. So remove that entry.
+import sys
+try:
+ del sys.modules['coverage.coverage']
+except KeyError:
+ pass
+
+
# COPYRIGHT AND LICENSE
#
# Copyright 2001 Gareth Rees. All rights reserved.
-# Copyright 2004-2010 Ned Batchelder. All rights reserved.
+# Copyright 2004-2013 Ned Batchelder. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
diff --git a/python/helpers/coverage/__main__.py b/python/helpers/coverage/__main__.py
index af5fa9f..55e0d25 100644
--- a/python/helpers/coverage/__main__.py
+++ b/python/helpers/coverage/__main__.py
@@ -1,3 +1,4 @@
-"""Coverage.py's main entrypoint."""
+"""Coverage.py's main entry point."""
+import sys
from coverage.cmdline import main
-main()
+sys.exit(main())
diff --git a/python/helpers/coverage/annotate.py b/python/helpers/coverage/annotate.py
index a556d85..5c39678 100644
--- a/python/helpers/coverage/annotate.py
+++ b/python/helpers/coverage/annotate.py
@@ -2,6 +2,7 @@
import os, re
+from coverage.backward import sorted # pylint: disable=W0622
from coverage.report import Reporter
class AnnotateReporter(Reporter):
@@ -26,20 +27,20 @@
"""
- def __init__(self, coverage, ignore_errors=False):
- super(AnnotateReporter, self).__init__(coverage, ignore_errors)
+ def __init__(self, coverage, config):
+ super(AnnotateReporter, self).__init__(coverage, config)
self.directory = None
blank_re = re.compile(r"\s*(#|$)")
else_re = re.compile(r"\s*else\s*:\s*(#|$)")
- def report(self, morfs, config, directory=None):
+ def report(self, morfs, directory=None):
"""Run the report.
See `coverage.report()` for arguments.
"""
- self.report_files(self.annotate_file, morfs, config, directory)
+ self.report_files(self.annotate_file, morfs, directory)
def annotate_file(self, cu, analysis):
"""Annotate a single file.
@@ -59,9 +60,9 @@
dest_file = filename + ",cover"
dest = open(dest_file, 'w')
- statements = analysis.statements
- missing = analysis.missing
- excluded = analysis.excluded
+ statements = sorted(analysis.statements)
+ missing = sorted(analysis.missing)
+ excluded = sorted(analysis.excluded)
lineno = 0
i = 0
diff --git a/python/helpers/coverage/backward.py b/python/helpers/coverage/backward.py
index f0a34ac..7d26854 100644
--- a/python/helpers/coverage/backward.py
+++ b/python/helpers/coverage/backward.py
@@ -6,7 +6,7 @@
# W0611: Unused import blah
# W0622: Redefining built-in blah
-import os, sys
+import os, re, sys
# Python 2.3 doesn't have `set`
try:
@@ -24,6 +24,31 @@
lst.sort()
return lst
+# Python 2.3 doesn't have `reversed`.
+try:
+ reversed = reversed
+except NameError:
+ def reversed(iterable):
+ """A 2.3-compatible implementation of `reversed`."""
+ lst = list(iterable)
+ return lst[::-1]
+
+# rpartition is new in 2.5
+try:
+ "".rpartition
+except AttributeError:
+ def rpartition(s, sep):
+ """Implement s.rpartition(sep) for old Pythons."""
+ i = s.rfind(sep)
+ if i == -1:
+ return ('', '', s)
+ else:
+ return (s[:i], sep, s[i+len(sep):])
+else:
+ def rpartition(s, sep):
+ """A common interface for new Pythons."""
+ return s.rpartition(sep)
+
# Pythons 2 and 3 differ on where to get StringIO
try:
from cStringIO import StringIO
@@ -49,6 +74,18 @@
except NameError:
range = range
+# A function to iterate listlessly over a dict's items.
+try:
+ {}.iteritems
+except AttributeError:
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.items()
+else:
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.iteritems()
+
# Exec is a statement in Py2, a function in Py3
if sys.version_info >= (3, 0):
def exec_code_object(code, global_map):
@@ -66,21 +103,32 @@
)
)
-# ConfigParser was renamed to the more-standard configparser
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
-
-# Python 3.2 provides `tokenize.open`, the best way to open source files.
-try:
+# Reading Python source and interpreting the coding comment is a big deal.
+if sys.version_info >= (3, 0):
+ # Python 3.2 provides `tokenize.open`, the best way to open source files.
import tokenize
- open_source = tokenize.open # pylint: disable=E1101
-except AttributeError:
+ try:
+ open_source = tokenize.open # pylint: disable=E1101
+ except AttributeError:
+ from io import TextIOWrapper
+ detect_encoding = tokenize.detect_encoding # pylint: disable=E1101
+ # Copied from the 3.2 stdlib:
+ def open_source(fname):
+ """Open a file in read only mode using the encoding detected by
+ detect_encoding().
+ """
+ buffer = open(fname, 'rb')
+ encoding, _ = detect_encoding(buffer.readline)
+ buffer.seek(0)
+ text = TextIOWrapper(buffer, encoding, line_buffering=True)
+ text.mode = 'r'
+ return text
+else:
def open_source(fname):
"""Open a source file the best way."""
return open(fname, "rU")
+
# Python 3.x is picky about bytes and strings, so provide methods to
# get them right, and make them no-ops in 2.x
if sys.version_info >= (3, 0):
@@ -92,6 +140,19 @@
"""Convert bytes `b` to a string."""
return b.decode('utf8')
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return bytes(byte_values)
+
+ def byte_to_int(byte_value):
+ """Turn an element of a bytes object into an int."""
+ return byte_value
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ # In Py3, iterating bytes gives ints.
+ return bytes_value
+
else:
def to_bytes(s):
"""Convert string `s` to bytes (no-op in 2.x)."""
@@ -101,6 +162,19 @@
"""Convert bytes `b` to a string (no-op in 2.x)."""
return b
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return "".join([chr(b) for b in byte_values])
+
+ def byte_to_int(byte_value):
+ """Turn an element of a bytes object into an int."""
+ return ord(byte_value)
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ for byte in bytes_value:
+ yield ord(byte)
+
# Md5 is available in different places.
try:
import hashlib
diff --git a/python/helpers/coverage/bytecode.py b/python/helpers/coverage/bytecode.py
index ab522d6..8536063 100644
--- a/python/helpers/coverage/bytecode.py
+++ b/python/helpers/coverage/bytecode.py
@@ -1,14 +1,25 @@
"""Bytecode manipulation for coverage.py"""
-import opcode, sys, types
+import opcode, types
+
+from coverage.backward import byte_to_int
class ByteCode(object):
"""A single bytecode."""
def __init__(self):
+ # The offset of this bytecode in the code object.
self.offset = -1
+
+ # The opcode, defined in the `opcode` module.
self.op = -1
+
+ # The argument, a small integer, whose meaning depends on the opcode.
self.arg = -1
+
+ # The offset in the code object of the next bytecode.
self.next_offset = -1
+
+ # The offset to jump to.
self.jump_to = -1
@@ -18,44 +29,34 @@
Returns `ByteCode` objects.
"""
+ # pylint: disable=R0924
def __init__(self, code):
self.code = code
- self.offset = 0
- if sys.version_info >= (3, 0):
- def __getitem__(self, i):
- return self.code[i]
- else:
- def __getitem__(self, i):
- return ord(self.code[i])
+ def __getitem__(self, i):
+ return byte_to_int(self.code[i])
def __iter__(self):
- return self
+ offset = 0
+ while offset < len(self.code):
+ bc = ByteCode()
+ bc.op = self[offset]
+ bc.offset = offset
- def __next__(self):
- if self.offset >= len(self.code):
- raise StopIteration
+ next_offset = offset+1
+ if bc.op >= opcode.HAVE_ARGUMENT:
+ bc.arg = self[offset+1] + 256*self[offset+2]
+ next_offset += 2
- bc = ByteCode()
- bc.op = self[self.offset]
- bc.offset = self.offset
+ label = -1
+ if bc.op in opcode.hasjrel:
+ label = next_offset + bc.arg
+ elif bc.op in opcode.hasjabs:
+ label = bc.arg
+ bc.jump_to = label
- next_offset = self.offset+1
- if bc.op >= opcode.HAVE_ARGUMENT:
- bc.arg = self[self.offset+1] + 256*self[self.offset+2]
- next_offset += 2
-
- label = -1
- if bc.op in opcode.hasjrel:
- label = next_offset + bc.arg
- elif bc.op in opcode.hasjabs:
- label = bc.arg
- bc.jump_to = label
-
- bc.next_offset = self.offset = next_offset
- return bc
-
- next = __next__ # Py2k uses an old-style non-dunder name.
+ bc.next_offset = offset = next_offset
+ yield bc
class CodeObjects(object):
@@ -64,18 +65,11 @@
self.stack = [code]
def __iter__(self):
- return self
-
- def __next__(self):
- if self.stack:
+ while self.stack:
# We're going to return the code object on the stack, but first
# push its children for later returning.
code = self.stack.pop()
for c in code.co_consts:
if isinstance(c, types.CodeType):
self.stack.append(c)
- return code
-
- raise StopIteration
-
- next = __next__
+ yield code
diff --git a/python/helpers/coverage/cmdline.py b/python/helpers/coverage/cmdline.py
index 1ce5e0f..ea112a8 100644
--- a/python/helpers/coverage/cmdline.py
+++ b/python/helpers/coverage/cmdline.py
@@ -1,10 +1,11 @@
"""Command-line support for Coverage."""
-import optparse, re, sys, traceback
+import optparse, os, sys, time, traceback
from coverage.backward import sorted # pylint: disable=W0622
from coverage.execfile import run_python_file, run_python_module
from coverage.misc import CoverageException, ExceptionDuringRun, NoSource
+from coverage.debug import info_formatter
class Opts(object):
@@ -19,11 +20,18 @@
'', '--branch', action='store_true',
help="Measure branch coverage in addition to statement coverage."
)
+ debug = optparse.make_option(
+ '', '--debug', action='store', metavar="OPTS",
+ help="Debug options, separated by commas"
+ )
directory = optparse.make_option(
- '-d', '--directory', action='store',
- metavar="DIR",
+ '-d', '--directory', action='store', metavar="DIR",
help="Write the output files to DIR."
)
+ fail_under = optparse.make_option(
+ '', '--fail-under', action='store', metavar="MIN", type="int",
+ help="Exit with a status of 2 if the total coverage is less than MIN."
+ )
help = optparse.make_option(
'-h', '--help', action='store_true',
help="Get help on this command."
@@ -89,6 +97,10 @@
help="Use a simpler but slower trace method. Try this if you get "
"seemingly impossible results!"
)
+ title = optparse.make_option(
+ '', '--title', action='store', metavar="TITLE",
+ help="A text string to use as the title on the HTML."
+ )
version = optparse.make_option(
'', '--version', action='store_true',
help="Display version information and exit."
@@ -110,7 +122,9 @@
self.set_defaults(
actions=[],
branch=None,
+ debug=None,
directory=None,
+ fail_under=None,
help=None,
ignore_errors=None,
include=None,
@@ -122,6 +136,7 @@
show_missing=None,
source=None,
timid=None,
+ title=None,
erase_first=None,
version=None,
)
@@ -273,9 +288,11 @@
'html': CmdOptionParser("html",
[
Opts.directory,
+ Opts.fail_under,
Opts.ignore_errors,
Opts.omit,
Opts.include,
+ Opts.title,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
description = "Create an HTML report of the coverage of the files. "
@@ -285,6 +302,7 @@
'report': CmdOptionParser("report",
[
+ Opts.fail_under,
Opts.ignore_errors,
Opts.omit,
Opts.include,
@@ -298,6 +316,7 @@
[
Opts.append,
Opts.branch,
+ Opts.debug,
Opts.pylib,
Opts.parallel_mode,
Opts.module,
@@ -314,20 +333,20 @@
'xml': CmdOptionParser("xml",
[
+ Opts.fail_under,
Opts.ignore_errors,
Opts.omit,
Opts.include,
Opts.output_xml,
] + GLOBAL_ARGS,
cmd = "xml",
- defaults = {'outfile': 'coverage.xml'},
usage = "[options] [modules]",
description = "Generate an XML report of coverage results."
),
}
-OK, ERR = 0, 1
+OK, ERR, FAIL_UNDER = 0, 1, 2
class CoverageScript(object):
@@ -346,27 +365,10 @@
self.run_python_file = _run_python_file or run_python_file
self.run_python_module = _run_python_module or run_python_module
self.help_fn = _help_fn or self.help
+ self.classic = False
self.coverage = None
- def help(self, error=None, topic=None, parser=None):
- """Display an error message, or the named topic."""
- assert error or topic or parser
- if error:
- print(error)
- print("Use 'coverage help' for help.")
- elif parser:
- print(parser.format_help().strip())
- else:
- # Parse out the topic we want from HELP_TOPICS
- topic_list = re.split("(?m)^=+ (\w+) =+$", HELP_TOPICS)
- topics = dict(zip(topic_list[1::2], topic_list[2::2]))
- help_msg = topics.get(topic, '').strip()
- if help_msg:
- print(help_msg % self.covpkg.__dict__)
- else:
- print("Don't know topic %r" % topic)
-
def command_line(self, argv):
"""The bulk of the command line interface to Coverage.
@@ -376,15 +378,14 @@
"""
# Collect the command-line options.
-
if not argv:
self.help_fn(topic='minimum_help')
return OK
# The command syntax we parse depends on the first argument. Classic
# syntax always starts with an option.
- classic = argv[0].startswith('-')
- if classic:
+ self.classic = argv[0].startswith('-')
+ if self.classic:
parser = ClassicOptionParser()
else:
parser = CMDS.get(argv[0])
@@ -398,64 +399,19 @@
if not ok:
return ERR
- # Handle help.
- if options.help:
- if classic:
- self.help_fn(topic='help')
- else:
- self.help_fn(parser=parser)
- return OK
-
- if "help" in options.actions:
- if args:
- for a in args:
- parser = CMDS.get(a)
- if parser:
- self.help_fn(parser=parser)
- else:
- self.help_fn(topic=a)
- else:
- self.help_fn(topic='help')
- return OK
-
- # Handle version.
- if options.version:
- self.help_fn(topic='version')
+ # Handle help and version.
+ if self.do_help(options, args, parser):
return OK
# Check for conflicts and problems in the options.
- for i in ['erase', 'execute']:
- for j in ['annotate', 'html', 'report', 'combine']:
- if (i in options.actions) and (j in options.actions):
- self.help_fn("You can't specify the '%s' and '%s' "
- "options at the same time." % (i, j))
- return ERR
-
- if not options.actions:
- self.help_fn(
- "You must specify at least one of -e, -x, -c, -r, -a, or -b."
- )
- return ERR
- args_allowed = (
- 'execute' in options.actions or
- 'annotate' in options.actions or
- 'html' in options.actions or
- 'debug' in options.actions or
- 'report' in options.actions or
- 'xml' in options.actions
- )
- if not args_allowed and args:
- self.help_fn("Unexpected arguments: %s" % " ".join(args))
- return ERR
-
- if 'execute' in options.actions and not args:
- self.help_fn("Nothing to do.")
+ if not self.args_ok(options, args):
return ERR
# Listify the list options.
source = unshell_list(options.source)
omit = unshell_list(options.omit)
include = unshell_list(options.include)
+ debug = unshell_list(options.debug)
# Do something.
self.coverage = self.covpkg.coverage(
@@ -467,41 +423,11 @@
source = source,
omit = omit,
include = include,
+ debug = debug,
)
if 'debug' in options.actions:
- if not args:
- self.help_fn("What information would you like: data, sys?")
- return ERR
- for info in args:
- if info == 'sys':
- print("-- sys ----------------------------------------")
- for label, info in self.coverage.sysinfo():
- if info == []:
- info = "-none-"
- if isinstance(info, list):
- print("%15s:" % label)
- for e in info:
- print("%15s %s" % ("", e))
- else:
- print("%15s: %s" % (label, info))
- elif info == 'data':
- print("-- data ---------------------------------------")
- self.coverage.load()
- print("path: %s" % self.coverage.data.filename)
- print("has_arcs: %r" % self.coverage.data.has_arcs())
- summary = self.coverage.data.summary(fullpath=True)
- if summary:
- filenames = sorted(summary.keys())
- print("\n%d files:" % len(filenames))
- for f in filenames:
- print("%s: %d lines" % (f, summary[f]))
- else:
- print("No data collected")
- else:
- self.help_fn("Don't know what you mean by %r" % info)
- return ERR
- return OK
+ return self.do_debug(args)
if 'erase' in options.actions or options.erase_first:
self.coverage.erase()
@@ -509,22 +435,7 @@
self.coverage.load()
if 'execute' in options.actions:
- # Run the script.
- self.coverage.start()
- code_ran = True
- try:
- try:
- if options.module:
- self.run_python_module(args[0], args)
- else:
- self.run_python_file(args[0], args)
- except NoSource:
- code_ran = False
- raise
- finally:
- if code_ran:
- self.coverage.stop()
- self.coverage.save()
+ self.do_execute(options, args)
if 'combine' in options.actions:
self.coverage.combine()
@@ -539,18 +450,167 @@
)
if 'report' in options.actions:
- self.coverage.report(
+ total = self.coverage.report(
show_missing=options.show_missing, **report_args)
if 'annotate' in options.actions:
self.coverage.annotate(
directory=options.directory, **report_args)
if 'html' in options.actions:
- self.coverage.html_report(
- directory=options.directory, **report_args)
+ total = self.coverage.html_report(
+ directory=options.directory, title=options.title,
+ **report_args)
if 'xml' in options.actions:
outfile = options.outfile
- self.coverage.xml_report(outfile=outfile, **report_args)
+ total = self.coverage.xml_report(outfile=outfile, **report_args)
+ if options.fail_under is not None:
+ if total >= options.fail_under:
+ return OK
+ else:
+ return FAIL_UNDER
+ else:
+ return OK
+
+ def help(self, error=None, topic=None, parser=None):
+ """Display an error message, or the named topic."""
+ assert error or topic or parser
+ if error:
+ print(error)
+ print("Use 'coverage help' for help.")
+ elif parser:
+ print(parser.format_help().strip())
+ else:
+ help_msg = HELP_TOPICS.get(topic, '').strip()
+ if help_msg:
+ print(help_msg % self.covpkg.__dict__)
+ else:
+ print("Don't know topic %r" % topic)
+
+ def do_help(self, options, args, parser):
+ """Deal with help requests.
+
+ Return True if it handled the request, False if not.
+
+ """
+ # Handle help.
+ if options.help:
+ if self.classic:
+ self.help_fn(topic='help')
+ else:
+ self.help_fn(parser=parser)
+ return True
+
+ if "help" in options.actions:
+ if args:
+ for a in args:
+ parser = CMDS.get(a)
+ if parser:
+ self.help_fn(parser=parser)
+ else:
+ self.help_fn(topic=a)
+ else:
+ self.help_fn(topic='help')
+ return True
+
+ # Handle version.
+ if options.version:
+ self.help_fn(topic='version')
+ return True
+
+ return False
+
+ def args_ok(self, options, args):
+ """Check for conflicts and problems in the options.
+
+ Returns True if everything is ok, or False if not.
+
+ """
+ for i in ['erase', 'execute']:
+ for j in ['annotate', 'html', 'report', 'combine']:
+ if (i in options.actions) and (j in options.actions):
+ self.help_fn("You can't specify the '%s' and '%s' "
+ "options at the same time." % (i, j))
+ return False
+
+ if not options.actions:
+ self.help_fn(
+ "You must specify at least one of -e, -x, -c, -r, -a, or -b."
+ )
+ return False
+ args_allowed = (
+ 'execute' in options.actions or
+ 'annotate' in options.actions or
+ 'html' in options.actions or
+ 'debug' in options.actions or
+ 'report' in options.actions or
+ 'xml' in options.actions
+ )
+ if not args_allowed and args:
+ self.help_fn("Unexpected arguments: %s" % " ".join(args))
+ return False
+
+ if 'execute' in options.actions and not args:
+ self.help_fn("Nothing to do.")
+ return False
+
+ return True
+
+ def do_execute(self, options, args):
+ """Implementation of 'coverage run'."""
+
+ # Set the first path element properly.
+ old_path0 = sys.path[0]
+
+ # Run the script.
+ self.coverage.start()
+ code_ran = True
+ try:
+ try:
+ if options.module:
+ sys.path[0] = ''
+ self.run_python_module(args[0], args)
+ else:
+ filename = args[0]
+ sys.path[0] = os.path.abspath(os.path.dirname(filename))
+ self.run_python_file(filename, args)
+ except NoSource:
+ code_ran = False
+ raise
+ finally:
+ self.coverage.stop()
+ if code_ran:
+ self.coverage.save()
+
+ # Restore the old path
+ sys.path[0] = old_path0
+
+ def do_debug(self, args):
+ """Implementation of 'coverage debug'."""
+
+ if not args:
+ self.help_fn("What information would you like: data, sys?")
+ return ERR
+ for info in args:
+ if info == 'sys':
+ print("-- sys ----------------------------------------")
+ for line in info_formatter(self.coverage.sysinfo()):
+ print(" %s" % line)
+ elif info == 'data':
+ print("-- data ---------------------------------------")
+ self.coverage.load()
+ print("path: %s" % self.coverage.data.filename)
+ print("has_arcs: %r" % self.coverage.data.has_arcs())
+ summary = self.coverage.data.summary(fullpath=True)
+ if summary:
+ filenames = sorted(summary.keys())
+ print("\n%d files:" % len(filenames))
+ for f in filenames:
+ print("%s: %d lines" % (f, summary[f]))
+ else:
+ print("No data collected")
+ else:
+ self.help_fn("Don't know what you mean by %r" % info)
+ return ERR
return OK
@@ -568,10 +628,10 @@
return s.split(',')
-HELP_TOPICS = r"""
-
-== classic ====================================================================
-Coverage.py version %(__version__)s
+HELP_TOPICS = {
+# -------------------------
+'classic':
+r"""Coverage.py version %(__version__)s
Measure, collect, and report on code coverage in Python programs.
Usage:
@@ -615,8 +675,9 @@
Coverage data is saved in the file .coverage by default. Set the
COVERAGE_FILE environment variable to save it somewhere else.
-
-== help =======================================================================
+""",
+# -------------------------
+'help': """\
Coverage.py, version %(__version__)s
Measure, collect, and report on code coverage in Python programs.
@@ -635,26 +696,32 @@
Use "coverage help <command>" for detailed help on any command.
Use "coverage help classic" for help on older command syntax.
For more information, see %(__url__)s
-
-== minimum_help ===============================================================
+""",
+# -------------------------
+'minimum_help': """\
Code coverage for Python. Use 'coverage help' for help.
-
-== version ====================================================================
+""",
+# -------------------------
+'version': """\
Coverage.py, version %(__version__)s. %(__url__)s
-
-"""
+""",
+}
def main(argv=None):
- """The main entrypoint to Coverage.
+ """The main entry point to Coverage.
- This is installed as the script entrypoint.
+ This is installed as the script entry point.
"""
if argv is None:
argv = sys.argv[1:]
try:
+ start = time.clock()
status = CoverageScript().command_line(argv)
+ end = time.clock()
+ if 0:
+ print("time: %.3fs" % (end - start))
except ExceptionDuringRun:
# An exception was caught while running the product code. The
# sys.exc_info() return tuple is packed into an ExceptionDuringRun
diff --git a/python/helpers/coverage/codeunit.py b/python/helpers/coverage/codeunit.py
index 55f44a2..ca1ae5c 100644
--- a/python/helpers/coverage/codeunit.py
+++ b/python/helpers/coverage/codeunit.py
@@ -52,8 +52,10 @@
else:
f = morf
# .pyc files should always refer to a .py instead.
- if f.endswith('.pyc'):
+ if f.endswith('.pyc') or f.endswith('.pyo'):
f = f[:-1]
+ elif f.endswith('$py.class'): # Jython
+ f = f[:-9] + ".py"
self.filename = self.file_locator.canonical_filename(f)
if hasattr(morf, '__name__'):
@@ -77,12 +79,18 @@
# Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
# of them defined.
- def __lt__(self, other): return self.name < other.name
- def __le__(self, other): return self.name <= other.name
- def __eq__(self, other): return self.name == other.name
- def __ne__(self, other): return self.name != other.name
- def __gt__(self, other): return self.name > other.name
- def __ge__(self, other): return self.name >= other.name
+ def __lt__(self, other):
+ return self.name < other.name
+ def __le__(self, other):
+ return self.name <= other.name
+ def __eq__(self, other):
+ return self.name == other.name
+ def __ne__(self, other):
+ return self.name != other.name
+ def __gt__(self, other):
+ return self.name > other.name
+ def __ge__(self, other):
+ return self.name >= other.name
def flat_rootname(self):
"""A base for a flat filename to correspond to this code unit.
@@ -113,5 +121,25 @@
# Couldn't find source.
raise CoverageException(
- "No source for code %r." % self.filename
+ "No source for code '%s'." % self.filename
)
+
+ def should_be_python(self):
+ """Does it seem like this file should contain Python?
+
+ This is used to decide if a file reported as part of the exection of
+ a program was really likely to have contained Python in the first
+ place.
+
+ """
+ # Get the file extension.
+ _, ext = os.path.splitext(self.filename)
+
+ # Anything named *.py* should be Python.
+ if ext.startswith('.py'):
+ return True
+ # A file with no extension should be Python.
+ if not ext:
+ return True
+ # Everything else is probably not Python.
+ return False
diff --git a/python/helpers/coverage/collector.py b/python/helpers/coverage/collector.py
index 9c40d16..8ba7d87 100644
--- a/python/helpers/coverage/collector.py
+++ b/python/helpers/coverage/collector.py
@@ -1,13 +1,24 @@
"""Raw data collector for Coverage."""
-import sys, threading
+import os, sys, threading
try:
# Use the C extension code when we can, for speed.
- from coverage.tracer import Tracer
+ from coverage.tracer import CTracer # pylint: disable=F0401,E0611
except ImportError:
# Couldn't import the C extension, maybe it isn't built.
- Tracer = None
+ if os.getenv('COVERAGE_TEST_TRACER') == 'c':
+ # During testing, we use the COVERAGE_TEST_TRACER env var to indicate
+ # that we've fiddled with the environment to test this fallback code.
+ # If we thought we had a C tracer, but couldn't import it, then exit
+ # quickly and clearly instead of dribbling confusing errors. I'm using
+ # sys.exit here instead of an exception because an exception here
+ # causes all sorts of other noise in unittest.
+ sys.stderr.write(
+ "*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n"
+ )
+ sys.exit(1)
+ CTracer = None
class PyTracer(object):
@@ -40,12 +51,19 @@
self.last_exc_back = None
self.last_exc_firstlineno = 0
self.arcs = False
+ self.thread = None
+ self.stopped = False
def _trace(self, frame, event, arg_unused):
"""The trace function passed to sys.settrace."""
- #print("trace event: %s %r @%d" % (
- # event, frame.f_code.co_filename, frame.f_lineno))
+ if self.stopped:
+ return
+
+ if 0:
+ sys.stderr.write("trace event: %s %r @%d\n" % (
+ event, frame.f_code.co_filename, frame.f_lineno
+ ))
if self.last_exc_back:
if frame == self.last_exc_back:
@@ -61,10 +79,11 @@
# in this file.
self.data_stack.append((self.cur_file_data, self.last_line))
filename = frame.f_code.co_filename
- tracename = self.should_trace_cache.get(filename)
- if tracename is None:
+ if filename not in self.should_trace_cache:
tracename = self.should_trace(filename, frame)
self.should_trace_cache[filename] = tracename
+ else:
+ tracename = self.should_trace_cache[filename]
#print("called, stack is %d deep, tracename is %r" % (
# len(self.data_stack), tracename))
if tracename:
@@ -105,15 +124,24 @@
Return a Python function suitable for use with sys.settrace().
"""
+ self.thread = threading.currentThread()
sys.settrace(self._trace)
return self._trace
def stop(self):
"""Stop this Tracer."""
+ self.stopped = True
+ if self.thread != threading.currentThread():
+ # Called on a different thread than started us: we can't unhook
+ # ourseves, but we've set the flag that we should stop, so we won't
+ # do any more tracing.
+ return
+
if hasattr(sys, "gettrace") and self.warn:
if sys.gettrace() != self._trace:
msg = "Trace function changed, measurement is likely wrong: %r"
- self.warn(msg % sys.gettrace())
+ self.warn(msg % (sys.gettrace(),))
+ #print("Stopping tracer on %s" % threading.current_thread().ident)
sys.settrace(None)
def get_stats(self):
@@ -146,7 +174,7 @@
"""Create a collector.
`should_trace` is a function, taking a filename, and returning a
- canonicalized filename, or False depending on whether the file should
+ canonicalized filename, or None depending on whether the file should
be traced or not.
If `timid` is true, then a slower simpler trace function will be
@@ -173,7 +201,7 @@
else:
# Being fast: use the C Tracer if it is available, else the Python
# trace function.
- self._trace_class = Tracer or PyTracer
+ self._trace_class = CTracer or PyTracer
def __repr__(self):
return "<Collector at 0x%x>" % id(self)
@@ -190,7 +218,7 @@
# A cache of the results from should_trace, the decision about whether
# to trace execution in a file. A dict of filename to (filename or
- # False).
+ # None).
self.should_trace_cache = {}
# Our active Tracers.
@@ -232,9 +260,29 @@
if self._collectors:
self._collectors[-1].pause()
self._collectors.append(self)
- #print >>sys.stderr, "Started: %r" % self._collectors
+ #print("Started: %r" % self._collectors, file=sys.stderr)
+
+ # Check to see whether we had a fullcoverage tracer installed.
+ traces0 = []
+ if hasattr(sys, "gettrace"):
+ fn0 = sys.gettrace()
+ if fn0:
+ tracer0 = getattr(fn0, '__self__', None)
+ if tracer0:
+ traces0 = getattr(tracer0, 'traces', [])
+
# Install the tracer on this thread.
- self._start_tracer()
+ fn = self._start_tracer()
+
+ for args in traces0:
+ (frame, event, arg), lineno = args
+ try:
+ fn(frame, event, arg, lineno=lineno)
+ except TypeError:
+ raise Exception(
+ "fullcoverage must be run with the C trace function."
+ )
+
# Install our installation tracer in threading, to jump start other
# threads.
threading.settrace(self._installation_trace)
diff --git a/python/helpers/coverage/config.py b/python/helpers/coverage/config.py
index 6b441dd..87318ff 100644
--- a/python/helpers/coverage/config.py
+++ b/python/helpers/coverage/config.py
@@ -1,7 +1,76 @@
"""Config file for coverage.py"""
-import os
-from coverage.backward import configparser # pylint: disable=W0622
+import os, re, sys
+from coverage.backward import string_class, iitems
+
+# In py3, # ConfigParser was renamed to the more-standard configparser
+try:
+ import configparser # pylint: disable=F0401
+except ImportError:
+ import ConfigParser as configparser
+
+
+class HandyConfigParser(configparser.RawConfigParser):
+ """Our specialization of ConfigParser."""
+
+ def read(self, filename):
+ """Read a filename as UTF-8 configuration data."""
+ kwargs = {}
+ if sys.version_info >= (3, 2):
+ kwargs['encoding'] = "utf-8"
+ return configparser.RawConfigParser.read(self, filename, **kwargs)
+
+ def get(self, *args, **kwargs):
+ v = configparser.RawConfigParser.get(self, *args, **kwargs)
+ def dollar_replace(m):
+ """Called for each $replacement."""
+ # Only one of the groups will have matched, just get its text.
+ word = [w for w in m.groups() if w is not None][0]
+ if word == "$":
+ return "$"
+ else:
+ return os.environ.get(word, '')
+
+ dollar_pattern = r"""(?x) # Use extended regex syntax
+ \$(?: # A dollar sign, then
+ (?P<v1>\w+) | # a plain word,
+ {(?P<v2>\w+)} | # or a {-wrapped word,
+ (?P<char>[$]) # or a dollar sign.
+ )
+ """
+ v = re.sub(dollar_pattern, dollar_replace, v)
+ return v
+
+ def getlist(self, section, option):
+ """Read a list of strings.
+
+ The value of `section` and `option` is treated as a comma- and newline-
+ separated list of strings. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ value_list = self.get(section, option)
+ values = []
+ for value_line in value_list.split('\n'):
+ for value in value_line.split(','):
+ value = value.strip()
+ if value:
+ values.append(value)
+ return values
+
+ def getlinelist(self, section, option):
+ """Read a list of full-line strings.
+
+ The value of `section` and `option` is treated as a newline-separated
+ list of strings. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ value_list = self.get(section, option)
+ return list(filter(None, value_list.split('\n')))
+
# The default line exclusion regexes
DEFAULT_EXCLUDE = [
@@ -29,9 +98,12 @@
operation of coverage.py.
"""
-
def __init__(self):
"""Initialize the configuration attributes to their defaults."""
+ # Metadata about the config.
+ self.attempted_config_files = []
+ self.config_files = []
+
# Defaults for [run]
self.branch = False
self.cover_pylib = False
@@ -39,6 +111,7 @@
self.parallel = False
self.timid = False
self.source = None
+ self.debug = []
# Defaults for [report]
self.exclude_list = DEFAULT_EXCLUDE[:]
@@ -48,13 +121,19 @@
self.partial_list = DEFAULT_PARTIAL[:]
self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
self.precision = 0
+ self.show_missing = False
# Defaults for [html]
self.html_dir = "htmlcov"
+ self.extra_css = None
+ self.html_title = "Coverage report"
# Defaults for [xml]
self.xml_output = "coverage.xml"
+ # Defaults for [paths]
+ self.paths = {}
+
def from_environment(self, env_var):
"""Read configuration from the `env_var` environment variable."""
# Timidity: for nose users, read an environment variable. This is a
@@ -64,93 +143,71 @@
if env:
self.timid = ('--timid' in env)
+ MUST_BE_LIST = ["omit", "include", "debug"]
+
def from_args(self, **kwargs):
"""Read config values from `kwargs`."""
- for k, v in kwargs.items():
+ for k, v in iitems(kwargs):
if v is not None:
+ if k in self.MUST_BE_LIST and isinstance(v, string_class):
+ v = [v]
setattr(self, k, v)
- def from_file(self, *files):
- """Read configuration from .rc files.
+ def from_file(self, filename):
+ """Read configuration from a .rc file.
- Each argument in `files` is a file name to read.
+ `filename` is a file name to read.
"""
- cp = configparser.RawConfigParser()
- cp.read(files)
+ self.attempted_config_files.append(filename)
+ cp = HandyConfigParser()
+ files_read = cp.read(filename)
+ if files_read is not None: # return value changed in 2.4
+ self.config_files.extend(files_read)
+
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ self.set_attr_from_config_option(cp, *option_spec)
+
+ # [paths] is special
+ if cp.has_section('paths'):
+ for option in cp.options('paths'):
+ self.paths[option] = cp.getlist('paths', option)
+
+ CONFIG_FILE_OPTIONS = [
# [run]
- if cp.has_option('run', 'branch'):
- self.branch = cp.getboolean('run', 'branch')
- if cp.has_option('run', 'cover_pylib'):
- self.cover_pylib = cp.getboolean('run', 'cover_pylib')
- if cp.has_option('run', 'data_file'):
- self.data_file = cp.get('run', 'data_file')
- if cp.has_option('run', 'include'):
- self.include = self.get_list(cp, 'run', 'include')
- if cp.has_option('run', 'omit'):
- self.omit = self.get_list(cp, 'run', 'omit')
- if cp.has_option('run', 'parallel'):
- self.parallel = cp.getboolean('run', 'parallel')
- if cp.has_option('run', 'source'):
- self.source = self.get_list(cp, 'run', 'source')
- if cp.has_option('run', 'timid'):
- self.timid = cp.getboolean('run', 'timid')
+ ('branch', 'run:branch', 'boolean'),
+ ('cover_pylib', 'run:cover_pylib', 'boolean'),
+ ('data_file', 'run:data_file'),
+ ('debug', 'run:debug', 'list'),
+ ('include', 'run:include', 'list'),
+ ('omit', 'run:omit', 'list'),
+ ('parallel', 'run:parallel', 'boolean'),
+ ('source', 'run:source', 'list'),
+ ('timid', 'run:timid', 'boolean'),
# [report]
- if cp.has_option('report', 'exclude_lines'):
- self.exclude_list = \
- self.get_line_list(cp, 'report', 'exclude_lines')
- if cp.has_option('report', 'ignore_errors'):
- self.ignore_errors = cp.getboolean('report', 'ignore_errors')
- if cp.has_option('report', 'include'):
- self.include = self.get_list(cp, 'report', 'include')
- if cp.has_option('report', 'omit'):
- self.omit = self.get_list(cp, 'report', 'omit')
- if cp.has_option('report', 'partial_branches'):
- self.partial_list = \
- self.get_line_list(cp, 'report', 'partial_branches')
- if cp.has_option('report', 'partial_branches_always'):
- self.partial_always_list = \
- self.get_line_list(cp, 'report', 'partial_branches_always')
- if cp.has_option('report', 'precision'):
- self.precision = cp.getint('report', 'precision')
+ ('exclude_list', 'report:exclude_lines', 'linelist'),
+ ('ignore_errors', 'report:ignore_errors', 'boolean'),
+ ('include', 'report:include', 'list'),
+ ('omit', 'report:omit', 'list'),
+ ('partial_list', 'report:partial_branches', 'linelist'),
+ ('partial_always_list', 'report:partial_branches_always', 'linelist'),
+ ('precision', 'report:precision', 'int'),
+ ('show_missing', 'report:show_missing', 'boolean'),
# [html]
- if cp.has_option('html', 'directory'):
- self.html_dir = cp.get('html', 'directory')
+ ('html_dir', 'html:directory'),
+ ('extra_css', 'html:extra_css'),
+ ('html_title', 'html:title'),
# [xml]
- if cp.has_option('xml', 'output'):
- self.xml_output = cp.get('xml', 'output')
+ ('xml_output', 'xml:output'),
+ ]
- def get_list(self, cp, section, option):
- """Read a list of strings from the ConfigParser `cp`.
-
- The value of `section` and `option` is treated as a comma- and newline-
- separated list of strings. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- value_list = cp.get(section, option)
- values = []
- for value_line in value_list.split('\n'):
- for value in value_line.split(','):
- value = value.strip()
- if value:
- values.append(value)
- return values
-
- def get_line_list(self, cp, section, option):
- """Read a list of full-line strings from the ConfigParser `cp`.
-
- The value of `section` and `option` is treated as a newline-separated
- list of strings. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- value_list = cp.get(section, option)
- return list(filter(None, value_list.split('\n')))
-
+ def set_attr_from_config_option(self, cp, attr, where, type_=''):
+ """Set an attribute on self if it exists in the ConfigParser."""
+ section, option = where.split(":")
+ if cp.has_option(section, option):
+ method = getattr(cp, 'get'+type_)
+ setattr(self, attr, method(section, option))
diff --git a/python/helpers/coverage/control.py b/python/helpers/coverage/control.py
index 5ca1ef9..f75a3dd 100644
--- a/python/helpers/coverage/control.py
+++ b/python/helpers/coverage/control.py
@@ -3,21 +3,31 @@
import atexit, os, random, socket, sys
from coverage.annotate import AnnotateReporter
-from coverage.backward import string_class
+from coverage.backward import string_class, iitems, sorted # pylint: disable=W0622
from coverage.codeunit import code_unit_factory, CodeUnit
from coverage.collector import Collector
from coverage.config import CoverageConfig
from coverage.data import CoverageData
+from coverage.debug import DebugControl
from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
-from coverage.files import find_python_files
+from coverage.files import PathAliases, find_python_files, prep_patterns
from coverage.html import HtmlReporter
from coverage.misc import CoverageException, bool_or_none, join_regex
+from coverage.misc import file_be_gone
from coverage.results import Analysis, Numbers
from coverage.summary import SummaryReporter
from coverage.xmlreport import XmlReporter
+# Pypy has some unusual stuff in the "stdlib". Consider those locations
+# when deciding where the stdlib is.
+try:
+ import _structseq # pylint: disable=F0401
+except ImportError:
+ _structseq = None
+
+
class coverage(object):
- """Programmatic access to Coverage.
+ """Programmatic access to coverage.py.
To use::
@@ -25,14 +35,15 @@
cov = coverage()
cov.start()
- #.. blah blah (run your code) blah blah ..
+ #.. call your code ..
cov.stop()
cov.html_report(directory='covhtml')
"""
def __init__(self, data_file=None, data_suffix=None, cover_pylib=None,
auto_data=False, timid=None, branch=None, config_file=True,
- source=None, omit=None, include=None):
+ source=None, omit=None, include=None, debug=None,
+ debug_file=None):
"""
`data_file` is the base name of the data file to use, defaulting to
".coverage". `data_suffix` is appended (with a dot) to `data_file` to
@@ -67,6 +78,10 @@
`include` will be measured, files that match `omit` will not. Each
will also accept a single string argument.
+ `debug` is a list of strings indicating what debugging information is
+ desired. `debug_file` is the file to write debug messages to,
+ defaulting to stderr.
+
"""
from coverage import __version__
@@ -96,18 +111,16 @@
self.config.data_file = env_data_file
# 4: from constructor arguments:
- if isinstance(omit, string_class):
- omit = [omit]
- if isinstance(include, string_class):
- include = [include]
self.config.from_args(
data_file=data_file, cover_pylib=cover_pylib, timid=timid,
branch=branch, parallel=bool_or_none(data_suffix),
- source=source, omit=omit, include=include
+ source=source, omit=omit, include=include, debug=debug,
)
+ # Create and configure the debugging controller.
+ self.debug = DebugControl(self.config.debug, debug_file or sys.stderr)
+
self.auto_data = auto_data
- self.atexit_registered = False
# _exclude_re is a dict mapping exclusion list names to compiled
# regexes.
@@ -125,8 +138,8 @@
else:
self.source_pkgs.append(src)
- self.omit = self._prep_patterns(self.config.omit)
- self.include = self._prep_patterns(self.config.include)
+ self.omit = prep_patterns(self.config.omit)
+ self.include = prep_patterns(self.config.include)
self.collector = Collector(
self._should_trace, timid=self.config.timid,
@@ -151,7 +164,8 @@
# started rather than wherever the process eventually chdir'd to.
self.data = CoverageData(
basename=self.config.data_file,
- collector="coverage v%s" % __version__
+ collector="coverage v%s" % __version__,
+ debug=self.debug,
)
# The dirs for files considered "installed with the interpreter".
@@ -162,9 +176,9 @@
# environments (virtualenv, for example), these modules may be
# spread across a few locations. Look at all the candidate modules
# we've imported, and take all the different ones.
- for m in (atexit, os, random, socket):
- if hasattr(m, "__file__"):
- m_dir = self._canonical_dir(m.__file__)
+ for m in (atexit, os, random, socket, _structseq):
+ if m is not None and hasattr(m, "__file__"):
+ m_dir = self._canonical_dir(m)
if m_dir not in self.pylib_dirs:
self.pylib_dirs.append(m_dir)
@@ -172,63 +186,61 @@
# where we are.
self.cover_dir = self._canonical_dir(__file__)
- # The matchers for _should_trace, created when tracing starts.
+ # The matchers for _should_trace.
self.source_match = None
self.pylib_match = self.cover_match = None
self.include_match = self.omit_match = None
- # Only _harvest_data once per measurement cycle.
- self._harvested = False
-
# Set the reporting precision.
Numbers.set_precision(self.config.precision)
- # When tearing down the coverage object, modules can become None.
- # Saving the modules as object attributes avoids problems, but it is
- # quite ad-hoc which modules need to be saved and which references
- # need to use the object attributes.
- self.socket = socket
- self.os = os
- self.random = random
+ # Is it ok for no data to be collected?
+ self._warn_no_data = True
+ self._warn_unimported_source = True
- def _canonical_dir(self, f):
- """Return the canonical directory of the file `f`."""
- return os.path.split(self.file_locator.canonical_filename(f))[0]
+ # State machine variables:
+ # Have we started collecting and not stopped it?
+ self._started = False
+ # Have we measured some data and not harvested it?
+ self._measured = False
+
+ atexit.register(self._atexit)
+
+ def _canonical_dir(self, morf):
+ """Return the canonical directory of the module or file `morf`."""
+ return os.path.split(CodeUnit(morf, self.file_locator).filename)[0]
def _source_for_file(self, filename):
"""Return the source file for `filename`."""
if not filename.endswith(".py"):
if filename[-4:-1] == ".py":
filename = filename[:-1]
+ elif filename.endswith("$py.class"): # jython
+ filename = filename[:-9] + ".py"
return filename
- def _should_trace(self, filename, frame):
- """Decide whether to trace execution in `filename`
+ def _should_trace_with_reason(self, filename, frame):
+ """Decide whether to trace execution in `filename`, with a reason.
This function is called from the trace function. As each new file name
is encountered, this function determines whether it is traced or not.
- Returns a canonicalized filename if it should be traced, False if it
- should not.
+ Returns a pair of values: the first indicates whether the file should
+ be traced: it's a canonicalized filename if it should be traced, None
+ if it should not. The second value is a string, the resason for the
+ decision.
"""
- if os is None:
- return False
+ if not filename:
+ # Empty string is pretty useless
+ return None, "empty string isn't a filename"
if filename.startswith('<'):
# Lots of non-file execution is represented with artificial
# filenames like "<string>", "<doctest readme.txt[0]>", or
# "<exec_function>". Don't ever trace these executions, since we
# can't do anything with the data later anyway.
- return False
-
- if filename.endswith(".html"):
- # Jinja and maybe other templating systems compile templates into
- # Python code, but use the template filename as the filename in
- # the compiled code. Of course, those filenames are useless later
- # so don't bother collecting. TODO: How should we really separate
- # out good file extensions from bad?
- return False
+ return None, "not a real filename"
self._check_for_packages()
@@ -248,64 +260,53 @@
canonical = self.file_locator.canonical_filename(filename)
- # If the user specified source, then that's authoritative about what to
- # measure. If they didn't, then we have to exclude the stdlib and
- # coverage.py directories.
+ # If the user specified source or include, then that's authoritative
+ # about the outer bound of what to measure and we don't have to apply
+ # any canned exclusions. If they didn't, then we have to exclude the
+ # stdlib and coverage.py directories.
if self.source_match:
if not self.source_match.match(canonical):
- return False
+ return None, "falls outside the --source trees"
+ elif self.include_match:
+ if not self.include_match.match(canonical):
+ return None, "falls outside the --include trees"
else:
# If we aren't supposed to trace installed code, then check if this
# is near the Python standard library and skip it if so.
if self.pylib_match and self.pylib_match.match(canonical):
- return False
+ return None, "is in the stdlib"
# We exclude the coverage code itself, since a little of it will be
# measured otherwise.
if self.cover_match and self.cover_match.match(canonical):
- return False
+ return None, "is part of coverage.py"
- # Check the file against the include and omit patterns.
- if self.include_match and not self.include_match.match(canonical):
- return False
+ # Check the file against the omit pattern.
if self.omit_match and self.omit_match.match(canonical):
- return False
+ return None, "is inside an --omit pattern"
+ return canonical, "because we love you"
+
+ def _should_trace(self, filename, frame):
+ """Decide whether to trace execution in `filename`.
+
+ Calls `_should_trace_with_reason`, and returns just the decision.
+
+ """
+ canonical, reason = self._should_trace_with_reason(filename, frame)
+ if self.debug.should('trace'):
+ if not canonical:
+ msg = "Not tracing %r: %s" % (filename, reason)
+ else:
+ msg = "Tracing %r" % (filename,)
+ self.debug.write(msg)
return canonical
- # To log what should_trace returns, change this to "if 1:"
- if 0:
- _real_should_trace = _should_trace
- def _should_trace(self, filename, frame): # pylint: disable=E0102
- """A logging decorator around the real _should_trace function."""
- ret = self._real_should_trace(filename, frame)
- print("should_trace: %r -> %r" % (filename, ret))
- return ret
-
def _warn(self, msg):
"""Use `msg` as a warning."""
self._warnings.append(msg)
sys.stderr.write("Coverage.py warning: %s\n" % msg)
- def _prep_patterns(self, patterns):
- """Prepare the file patterns for use in a `FnmatchMatcher`.
-
- If a pattern starts with a wildcard, it is used as a pattern
- as-is. If it does not start with a wildcard, then it is made
- absolute with the current directory.
-
- If `patterns` is None, an empty list is returned.
-
- """
- patterns = patterns or []
- prepped = []
- for p in patterns or []:
- if p.startswith("*") or p.startswith("?"):
- prepped.append(p)
- else:
- prepped.append(self.file_locator.abs_file(p))
- return prepped
-
def _check_for_packages(self):
"""Update the source_match matcher with latest imported packages."""
# Our self.source_pkgs attribute is a list of package names we want to
@@ -325,17 +326,23 @@
try:
pkg_file = mod.__file__
except AttributeError:
- self._warn("Module %s has no Python source." % pkg)
+ pkg_file = None
else:
d, f = os.path.split(pkg_file)
- if f.startswith('__init__.'):
+ if f.startswith('__init__'):
# This is actually a package, return the directory.
pkg_file = d
else:
pkg_file = self._source_for_file(pkg_file)
pkg_file = self.file_locator.canonical_filename(pkg_file)
+ if not os.path.exists(pkg_file):
+ pkg_file = None
+
+ if pkg_file:
self.source.append(pkg_file)
self.source_match.add(pkg_file)
+ else:
+ self._warn("Module %s has no Python source." % pkg)
for pkg in found:
self.source_pkgs.remove(pkg)
@@ -354,17 +361,21 @@
self.data.read()
def start(self):
- """Start measuring code coverage."""
+ """Start measuring code coverage.
+
+ Coverage measurement actually occurs in functions called after `start`
+ is invoked. Statements in the same scope as `start` won't be measured.
+
+ Once you invoke `start`, you must also call `stop` eventually, or your
+ process might not shut down cleanly.
+
+ """
if self.run_suffix:
# Calling start() means we're running code, so use the run_suffix
# as the data_suffix when we eventually save the data.
self.data_suffix = self.run_suffix
if self.auto_data:
self.load()
- # Save coverage data when Python exits.
- if not self.atexit_registered:
- atexit.register(self.save)
- self.atexit_registered = True
# Create the matchers we need for _should_trace
if self.source or self.source_pkgs:
@@ -379,13 +390,31 @@
if self.omit:
self.omit_match = FnmatchMatcher(self.omit)
- self._harvested = False
+ # The user may want to debug things, show info if desired.
+ if self.debug.should('config'):
+ self.debug.write("Configuration values:")
+ config_info = sorted(self.config.__dict__.items())
+ self.debug.write_formatted_info(config_info)
+
+ if self.debug.should('sys'):
+ self.debug.write("Debugging info:")
+ self.debug.write_formatted_info(self.sysinfo())
+
self.collector.start()
+ self._started = True
+ self._measured = True
def stop(self):
"""Stop measuring code coverage."""
+ self._started = False
self.collector.stop()
- self._harvest_data()
+
+ def _atexit(self):
+ """Clean up on process shutdown."""
+ if self._started:
+ self.stop()
+ if self.auto_data:
+ self.save()
def erase(self):
"""Erase previously-collected coverage data.
@@ -449,9 +478,15 @@
# plenty of distinguishing information. We do this here in
# `save()` at the last minute so that the pid will be correct even
# if the process forks.
- data_suffix = "%s.%s.%06d" % (
- self.socket.gethostname(), self.os.getpid(),
- self.random.randint(0, 99999)
+ extra = ""
+ if _TEST_NAME_FILE:
+ f = open(_TEST_NAME_FILE)
+ test_name = f.read()
+ f.close()
+ extra = "." + test_name
+ data_suffix = "%s%s.%s.%06d" % (
+ socket.gethostname(), extra, os.getpid(),
+ random.randint(0, 999999)
)
self._harvest_data()
@@ -465,7 +500,14 @@
current measurements.
"""
- self.data.combine_parallel_data()
+ aliases = None
+ if self.config.paths:
+ aliases = PathAliases(self.file_locator)
+ for paths in self.config.paths.values():
+ result = paths[0]
+ for pattern in paths[1:]:
+ aliases.add(pattern, result)
+ self.data.combine_parallel_data(aliases=aliases)
def _harvest_data(self):
"""Get the collected data and reset the collector.
@@ -473,27 +515,37 @@
Also warn about various problems collecting data.
"""
- if not self._harvested:
- self.data.add_line_data(self.collector.get_line_data())
- self.data.add_arc_data(self.collector.get_arc_data())
- self.collector.reset()
+ if not self._measured:
+ return
- # If there are still entries in the source_pkgs list, then we never
- # encountered those packages.
+ self.data.add_line_data(self.collector.get_line_data())
+ self.data.add_arc_data(self.collector.get_arc_data())
+ self.collector.reset()
+
+ # If there are still entries in the source_pkgs list, then we never
+ # encountered those packages.
+ if self._warn_unimported_source:
for pkg in self.source_pkgs:
self._warn("Module %s was never imported." % pkg)
- # Find out if we got any data.
- summary = self.data.summary()
- if not summary:
- self._warn("No data was collected.")
+ # Find out if we got any data.
+ summary = self.data.summary()
+ if not summary and self._warn_no_data:
+ self._warn("No data was collected.")
- # Find files that were never executed at all.
- for src in self.source:
- for py_file in find_python_files(src):
- self.data.touch_file(py_file)
+ # Find files that were never executed at all.
+ for src in self.source:
+ for py_file in find_python_files(src):
+ py_file = self.file_locator.canonical_filename(py_file)
- self._harvested = True
+ if self.omit_match and self.omit_match.match(py_file):
+ # Turns out this file was omitted, so don't pull it back
+ # in as unexecuted.
+ continue
+
+ self.data.touch_file(py_file)
+
+ self._measured = False
# Backward compatibility with version 1.
def analysis(self, morf):
@@ -520,8 +572,11 @@
"""
analysis = self._analyze(morf)
return (
- analysis.filename, analysis.statements, analysis.excluded,
- analysis.missing, analysis.missing_formatted()
+ analysis.filename,
+ sorted(analysis.statements),
+ sorted(analysis.excluded),
+ sorted(analysis.missing),
+ analysis.missing_formatted(),
)
def _analyze(self, it):
@@ -530,6 +585,7 @@
Returns an `Analysis` object.
"""
+ self._harvest_data()
if not isinstance(it, CodeUnit):
it = code_unit_factory(it, self.file_locator)[0]
@@ -548,14 +604,16 @@
match those patterns will be included in the report. Modules matching
`omit` will not be included in the report.
+ Returns a float, the total percentage covered.
+
"""
+ self._harvest_data()
self.config.from_args(
- ignore_errors=ignore_errors, omit=omit, include=include
+ ignore_errors=ignore_errors, omit=omit, include=include,
+ show_missing=show_missing,
)
- reporter = SummaryReporter(
- self, show_missing, self.config.ignore_errors
- )
- reporter.report(morfs, outfile=file, config=self.config)
+ reporter = SummaryReporter(self, self.config)
+ return reporter.report(morfs, outfile=file)
def annotate(self, morfs=None, directory=None, ignore_errors=None,
omit=None, include=None):
@@ -569,25 +627,39 @@
See `coverage.report()` for other arguments.
"""
+ self._harvest_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include
)
- reporter = AnnotateReporter(self, self.config.ignore_errors)
- reporter.report(morfs, config=self.config, directory=directory)
+ reporter = AnnotateReporter(self, self.config)
+ reporter.report(morfs, directory=directory)
def html_report(self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None):
+ omit=None, include=None, extra_css=None, title=None):
"""Generate an HTML report.
+ The HTML is written to `directory`. The file "index.html" is the
+ overview starting point, with links to more detailed pages for
+ individual modules.
+
+ `extra_css` is a path to a file of other CSS to apply on the page.
+ It will be copied into the HTML directory.
+
+ `title` is a text string (not HTML) to use as the title of the HTML
+ report.
+
See `coverage.report()` for other arguments.
+ Returns a float, the total percentage covered.
+
"""
+ self._harvest_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include,
- html_dir=directory,
+ html_dir=directory, extra_css=extra_css, html_title=title,
)
- reporter = HtmlReporter(self, self.config.ignore_errors)
- reporter.report(morfs, config=self.config)
+ reporter = HtmlReporter(self, self.config)
+ return reporter.report(morfs)
def xml_report(self, morfs=None, outfile=None, ignore_errors=None,
omit=None, include=None):
@@ -600,12 +672,16 @@
See `coverage.report()` for other arguments.
+ Returns a float, the total percentage covered.
+
"""
+ self._harvest_data()
self.config.from_args(
ignore_errors=ignore_errors, omit=omit, include=include,
xml_output=outfile,
)
file_to_close = None
+ delete_file = False
if self.config.xml_output:
if self.config.xml_output == '-':
outfile = sys.stdout
@@ -613,11 +689,17 @@
outfile = open(self.config.xml_output, "w")
file_to_close = outfile
try:
- reporter = XmlReporter(self, self.config.ignore_errors)
- reporter.report(morfs, outfile=outfile, config=self.config)
+ try:
+ reporter = XmlReporter(self, self.config)
+ return reporter.report(morfs, outfile=outfile)
+ except CoverageException:
+ delete_file = True
+ raise
finally:
if file_to_close:
file_to_close.close()
+ if delete_file:
+ file_be_gone(self.config.xml_output)
def sysinfo(self):
"""Return a list of (key, value) pairs showing internal information."""
@@ -625,22 +707,43 @@
import coverage as covmod
import platform, re
+ try:
+ implementation = platform.python_implementation()
+ except AttributeError:
+ implementation = "unknown"
+
info = [
('version', covmod.__version__),
('coverage', covmod.__file__),
('cover_dir', self.cover_dir),
('pylib_dirs', self.pylib_dirs),
('tracer', self.collector.tracer_name()),
+ ('config_files', self.config.attempted_config_files),
+ ('configs_read', self.config.config_files),
('data_path', self.data.filename),
('python', sys.version.replace('\n', '')),
('platform', platform.platform()),
+ ('implementation', implementation),
+ ('executable', sys.executable),
('cwd', os.getcwd()),
('path', sys.path),
- ('environment', [
- ("%s = %s" % (k, v)) for k, v in os.environ.items()
- if re.search("^COV|^PY", k)
- ]),
+ ('environment', sorted([
+ ("%s = %s" % (k, v)) for k, v in iitems(os.environ)
+ if re.search(r"^COV|^PY", k)
+ ])),
+ ('command_line', " ".join(getattr(sys, 'argv', ['???']))),
]
+ if self.source_match:
+ info.append(('source_match', self.source_match.info()))
+ if self.include_match:
+ info.append(('include_match', self.include_match.info()))
+ if self.omit_match:
+ info.append(('omit_match', self.omit_match.info()))
+ if self.cover_match:
+ info.append(('cover_match', self.cover_match.info()))
+ if self.pylib_match:
+ info.append(('pylib_match', self.pylib_match.info()))
+
return info
@@ -667,7 +770,10 @@
cps = os.environ.get("COVERAGE_PROCESS_START")
if cps:
cov = coverage(config_file=cps, auto_data=True)
- if os.environ.get("COVERAGE_COVERAGE"):
- # Measuring coverage within coverage.py takes yet more trickery.
- cov.cover_dir = "Please measure coverage.py!"
cov.start()
+ cov._warn_no_data = False
+ cov._warn_unimported_source = False
+
+
+# A hack for debugging testing in subprocesses.
+_TEST_NAME_FILE = "" #"/tmp/covtest.txt"
diff --git a/python/helpers/coverage/data.py b/python/helpers/coverage/data.py
index 3263cb3..fb88c5b 100644
--- a/python/helpers/coverage/data.py
+++ b/python/helpers/coverage/data.py
@@ -2,7 +2,9 @@
import os
-from coverage.backward import pickle, sorted # pylint: disable=W0622
+from coverage.backward import iitems, pickle, sorted # pylint: disable=W0622
+from coverage.files import PathAliases
+from coverage.misc import file_be_gone
class CoverageData(object):
@@ -21,15 +23,18 @@
"""
- def __init__(self, basename=None, collector=None):
+ def __init__(self, basename=None, collector=None, debug=None):
"""Create a CoverageData.
`basename` is the name of the file to use for storing data.
`collector` is a string describing the coverage measurement software.
+ `debug` is a `DebugControl` object for writing debug messages.
+
"""
self.collector = collector or 'unknown'
+ self.debug = debug
self.use_file = True
@@ -59,10 +64,6 @@
#
self.arcs = {}
- self.os = os
- self.sorted = sorted
- self.pickle = pickle
-
def usefile(self, use_file=True):
"""Set whether or not to use a disk file for data."""
self.use_file = use_file
@@ -92,21 +93,21 @@
def erase(self):
"""Erase the data, both in this object, and from its file storage."""
if self.use_file:
- if self.filename and os.path.exists(self.filename):
- os.remove(self.filename)
+ if self.filename:
+ file_be_gone(self.filename)
self.lines = {}
self.arcs = {}
def line_data(self):
"""Return the map from filenames to lists of line numbers executed."""
return dict(
- [(f, self.sorted(lmap.keys())) for f, lmap in self.lines.items()]
+ [(f, sorted(lmap.keys())) for f, lmap in iitems(self.lines)]
)
def arc_data(self):
"""Return the map from filenames to lists of line number pairs."""
return dict(
- [(f, self.sorted(amap.keys())) for f, amap in self.arcs.items()]
+ [(f, sorted(amap.keys())) for f, amap in iitems(self.arcs)]
)
def write_file(self, filename):
@@ -123,10 +124,13 @@
if self.collector:
data['collector'] = self.collector
+ if self.debug and self.debug.should('dataio'):
+ self.debug.write("Writing data to %r" % (filename,))
+
# Write the pickle to the file.
fdata = open(filename, 'wb')
try:
- self.pickle.dump(data, fdata, 2)
+ pickle.dump(data, fdata, 2)
finally:
fdata.close()
@@ -136,6 +140,8 @@
def raw_data(self, filename):
"""Return the raw pickled data from `filename`."""
+ if self.debug and self.debug.should('dataio'):
+ self.debug.write("Reading data from %r" % (filename,))
fdata = open(filename, 'rb')
try:
data = pickle.load(fdata)
@@ -158,33 +164,39 @@
# Unpack the 'lines' item.
lines = dict([
(f, dict.fromkeys(linenos, None))
- for f, linenos in data.get('lines', {}).items()
+ for f, linenos in iitems(data.get('lines', {}))
])
# Unpack the 'arcs' item.
arcs = dict([
(f, dict.fromkeys(arcpairs, None))
- for f, arcpairs in data.get('arcs', {}).items()
+ for f, arcpairs in iitems(data.get('arcs', {}))
])
except Exception:
pass
return lines, arcs
- def combine_parallel_data(self):
+ def combine_parallel_data(self, aliases=None):
"""Combine a number of data files together.
Treat `self.filename` as a file prefix, and combine the data from all
of the data files starting with that prefix plus a dot.
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+
"""
+ aliases = aliases or PathAliases()
data_dir, local = os.path.split(self.filename)
localdot = local + '.'
for f in os.listdir(data_dir or '.'):
if f.startswith(localdot):
full_path = os.path.join(data_dir, f)
new_lines, new_arcs = self._read_file(full_path)
- for filename, file_data in new_lines.items():
+ for filename, file_data in iitems(new_lines):
+ filename = aliases.map(filename)
self.lines.setdefault(filename, {}).update(file_data)
- for filename, file_data in new_arcs.items():
+ for filename, file_data in iitems(new_arcs):
+ filename = aliases.map(filename)
self.arcs.setdefault(filename, {}).update(file_data)
if f != local:
os.remove(full_path)
@@ -195,7 +207,7 @@
`line_data` is { filename: { lineno: None, ... }, ...}
"""
- for filename, linenos in line_data.items():
+ for filename, linenos in iitems(line_data):
self.lines.setdefault(filename, {}).update(linenos)
def add_arc_data(self, arc_data):
@@ -204,7 +216,7 @@
`arc_data` is { filename: { (l1,l2): None, ... }, ...}
"""
- for filename, arcs in arc_data.items():
+ for filename, arcs in iitems(arc_data):
self.arcs.setdefault(filename, {}).update(arcs)
def touch_file(self, filename):
@@ -245,8 +257,8 @@
if fullpath:
filename_fn = lambda f: f
else:
- filename_fn = self.os.path.basename
- for filename, lines in self.lines.items():
+ filename_fn = os.path.basename
+ for filename, lines in iitems(self.lines):
summ[filename_fn(filename)] = len(lines)
return summ
diff --git a/python/helpers/coverage/debug.py b/python/helpers/coverage/debug.py
new file mode 100644
index 0000000..104f3b1
--- /dev/null
+++ b/python/helpers/coverage/debug.py
@@ -0,0 +1,54 @@
+"""Control of and utilities for debugging."""
+
+import os
+
+
+# When debugging, it can be helpful to force some options, especially when
+# debugging the configuration mechanisms you usually use to control debugging!
+# This is a list of forced debugging options.
+FORCED_DEBUG = []
+
+
+class DebugControl(object):
+ """Control and output for debugging."""
+
+ def __init__(self, options, output):
+ """Configure the options and output file for debugging."""
+ self.options = options
+ self.output = output
+
+ def should(self, option):
+ """Decide whether to output debug information in category `option`."""
+ return (option in self.options or option in FORCED_DEBUG)
+
+ def write(self, msg):
+ """Write a line of debug output."""
+ if self.should('pid'):
+ msg = "pid %5d: %s" % (os.getpid(), msg)
+ self.output.write(msg+"\n")
+ self.output.flush()
+
+ def write_formatted_info(self, info):
+ """Write a sequence of (label,data) pairs nicely."""
+ for line in info_formatter(info):
+ self.write(" %s" % line)
+
+
+def info_formatter(info):
+ """Produce a sequence of formatted lines from info.
+
+ `info` is a sequence of pairs (label, data). The produced lines are
+ nicely formatted, ready to print.
+
+ """
+ label_len = max([len(l) for l, _d in info])
+ for label, data in info:
+ if data == []:
+ data = "-none-"
+ if isinstance(data, (list, tuple)):
+ prefix = "%*s:" % (label_len, label)
+ for e in data:
+ yield "%*s %s" % (label_len+1, prefix, e)
+ prefix = ""
+ else:
+ yield "%*s: %s" % (label_len, label, data)
diff --git a/python/helpers/coverage/execfile.py b/python/helpers/coverage/execfile.py
index 71227b7..f6ebdf7 100644
--- a/python/helpers/coverage/execfile.py
+++ b/python/helpers/coverage/execfile.py
@@ -1,9 +1,9 @@
"""Execute files of Python code."""
-import imp, os, sys
+import imp, marshal, os, sys
from coverage.backward import exec_code_object, open_source
-from coverage.misc import NoSource, ExceptionDuringRun
+from coverage.misc import ExceptionDuringRun, NoCode, NoSource
try:
@@ -65,6 +65,8 @@
openfile.close()
# Finally, hand the file off to run_python_file for execution.
+ pathname = os.path.abspath(pathname)
+ args[0] = pathname
run_python_file(pathname, args, package=packagename)
@@ -82,34 +84,22 @@
main_mod = imp.new_module('__main__')
sys.modules['__main__'] = main_mod
main_mod.__file__ = filename
- main_mod.__package__ = package
+ if package:
+ main_mod.__package__ = package
main_mod.__builtins__ = BUILTINS
- # Set sys.argv and the first path element properly.
+ # Set sys.argv properly.
old_argv = sys.argv
- old_path0 = sys.path[0]
sys.argv = args
- sys.path[0] = os.path.abspath(os.path.dirname(filename))
try:
- # Open the source file.
- try:
- source_file = open_source(filename)
- except IOError:
- raise NoSource("No file to run: %r" % filename)
+ # Make a code object somehow.
+ if filename.endswith(".pyc") or filename.endswith(".pyo"):
+ code = make_code_from_pyc(filename)
+ else:
+ code = make_code_from_py(filename)
- try:
- source = source_file.read()
- finally:
- source_file.close()
-
- # We have the source. `compile` still needs the last line to be clean,
- # so make sure it is, then compile a code object from it.
- if source[-1] != '\n':
- source += '\n'
- code = compile(source, filename, "exec")
-
- # Execute the source file.
+ # Execute the code object.
try:
exec_code_object(code, main_mod.__dict__)
except SystemExit:
@@ -130,4 +120,52 @@
# Restore the old argv and path
sys.argv = old_argv
- sys.path[0] = old_path0
+
+def make_code_from_py(filename):
+ """Get source from `filename` and make a code object of it."""
+ # Open the source file.
+ try:
+ source_file = open_source(filename)
+ except IOError:
+ raise NoSource("No file to run: %r" % filename)
+
+ try:
+ source = source_file.read()
+ finally:
+ source_file.close()
+
+ # We have the source. `compile` still needs the last line to be clean,
+ # so make sure it is, then compile a code object from it.
+ if not source or source[-1] != '\n':
+ source += '\n'
+ code = compile(source, filename, "exec")
+
+ return code
+
+
+def make_code_from_pyc(filename):
+ """Get a code object from a .pyc file."""
+ try:
+ fpyc = open(filename, "rb")
+ except IOError:
+ raise NoCode("No file to run: %r" % filename)
+
+ try:
+ # First four bytes are a version-specific magic number. It has to
+ # match or we won't run the file.
+ magic = fpyc.read(4)
+ if magic != imp.get_magic():
+ raise NoCode("Bad magic number in .pyc file")
+
+ # Skip the junk in the header that we don't need.
+ fpyc.read(4) # Skip the moddate.
+ if sys.version_info >= (3, 3):
+ # 3.3 added another long to the header (size), skip it.
+ fpyc.read(4)
+
+ # The rest of the file is the code object we want.
+ code = marshal.load(fpyc)
+ finally:
+ fpyc.close()
+
+ return code
diff --git a/python/helpers/coverage/files.py b/python/helpers/coverage/files.py
index a68a0a7..464535a 100644
--- a/python/helpers/coverage/files.py
+++ b/python/helpers/coverage/files.py
@@ -1,23 +1,21 @@
"""File wrangling."""
from coverage.backward import to_string
-import fnmatch, os, sys
+from coverage.misc import CoverageException
+import fnmatch, os, os.path, re, sys
+import ntpath, posixpath
class FileLocator(object):
"""Understand how filenames work."""
def __init__(self):
# The absolute path to our current directory.
- self.relative_dir = self.abs_file(os.curdir) + os.sep
+ self.relative_dir = os.path.normcase(abs_file(os.curdir) + os.sep)
# Cache of results of calling the canonical_filename() method, to
# avoid duplicating work.
self.canonical_filename_cache = {}
- def abs_file(self, filename):
- """Return the absolute normalized form of `filename`."""
- return os.path.normcase(os.path.abspath(os.path.realpath(filename)))
-
def relative_filename(self, filename):
"""Return the relative form of `filename`.
@@ -25,8 +23,9 @@
`FileLocator` was constructed.
"""
- if filename.startswith(self.relative_dir):
- filename = filename.replace(self.relative_dir, "")
+ fnorm = os.path.normcase(filename)
+ if fnorm.startswith(self.relative_dir):
+ filename = filename[len(self.relative_dir):]
return filename
def canonical_filename(self, filename):
@@ -36,19 +35,15 @@
"""
if filename not in self.canonical_filename_cache:
- f = filename
- if os.path.isabs(f) and not os.path.exists(f):
- if self.get_zip_data(f) is None:
- f = os.path.basename(f)
- if not os.path.isabs(f):
+ if not os.path.isabs(filename):
for path in [os.curdir] + sys.path:
if path is None:
continue
- g = os.path.join(path, f)
- if os.path.exists(g):
- f = g
+ f = os.path.join(path, filename)
+ if os.path.exists(f):
+ filename = f
break
- cf = self.abs_file(f)
+ cf = abs_file(filename)
self.canonical_filename_cache[filename] = cf
return self.canonical_filename_cache[filename]
@@ -77,6 +72,78 @@
return None
+if sys.platform == 'win32':
+
+ def actual_path(path):
+ """Get the actual path of `path`, including the correct case."""
+ if path in actual_path.cache:
+ return actual_path.cache[path]
+
+ head, tail = os.path.split(path)
+ if not tail:
+ actpath = head
+ elif not head:
+ actpath = tail
+ else:
+ head = actual_path(head)
+ if head in actual_path.list_cache:
+ files = actual_path.list_cache[head]
+ else:
+ try:
+ files = os.listdir(head)
+ except OSError:
+ files = []
+ actual_path.list_cache[head] = files
+ normtail = os.path.normcase(tail)
+ for f in files:
+ if os.path.normcase(f) == normtail:
+ tail = f
+ break
+ actpath = os.path.join(head, tail)
+ actual_path.cache[path] = actpath
+ return actpath
+
+ actual_path.cache = {}
+ actual_path.list_cache = {}
+
+else:
+ def actual_path(filename):
+ """The actual path for non-Windows platforms."""
+ return filename
+
+
+def abs_file(filename):
+ """Return the absolute normalized form of `filename`."""
+ path = os.path.expandvars(os.path.expanduser(filename))
+ path = os.path.abspath(os.path.realpath(path))
+ path = actual_path(path)
+ return path
+
+
+def isabs_anywhere(filename):
+ """Is `filename` an absolute path on any OS?"""
+ return ntpath.isabs(filename) or posixpath.isabs(filename)
+
+
+def prep_patterns(patterns):
+ """Prepare the file patterns for use in a `FnmatchMatcher`.
+
+ If a pattern starts with a wildcard, it is used as a pattern
+ as-is. If it does not start with a wildcard, then it is made
+ absolute with the current directory.
+
+ If `patterns` is None, an empty list is returned.
+
+ """
+ prepped = []
+ for p in patterns or []:
+ if p.startswith("*") or p.startswith("?"):
+ prepped.append(p)
+ else:
+ prepped.append(abs_file(p))
+ return prepped
+
+
class TreeMatcher(object):
"""A matcher for files in a tree."""
def __init__(self, directories):
@@ -85,6 +152,10 @@
def __repr__(self):
return "<TreeMatcher %r>" % self.dirs
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.dirs
+
def add(self, directory):
"""Add another directory to the list we match for."""
self.dirs.append(directory)
@@ -110,6 +181,10 @@
def __repr__(self):
return "<FnmatchMatcher %r>" % self.pats
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.pats
+
def match(self, fpath):
"""Does `fpath` match one of our filename patterns?"""
for pat in self.pats:
@@ -118,14 +193,117 @@
return False
+def sep(s):
+ """Find the path separator used in this string, or os.sep if none."""
+ sep_match = re.search(r"[\\/]", s)
+ if sep_match:
+ the_sep = sep_match.group(0)
+ else:
+ the_sep = os.sep
+ return the_sep
+
+
+class PathAliases(object):
+ """A collection of aliases for paths.
+
+ When combining data files from remote machines, often the paths to source
+ code are different, for example, due to OS differences, or because of
+ serialized checkouts on continuous integration machines.
+
+ A `PathAliases` object tracks a list of pattern/result pairs, and can
+ map a path through those aliases to produce a unified path.
+
+ `locator` is a FileLocator that is used to canonicalize the results.
+
+ """
+ def __init__(self, locator=None):
+ self.aliases = []
+ self.locator = locator
+
+ def add(self, pattern, result):
+ """Add the `pattern`/`result` pair to the list of aliases.
+
+ `pattern` is an `fnmatch`-style pattern. `result` is a simple
+ string. When mapping paths, if a path starts with a match against
+ `pattern`, then that match is replaced with `result`. This models
+ isomorphic source trees being rooted at different places on two
+ different machines.
+
+ `pattern` can't end with a wildcard component, since that would
+ match an entire tree, and not just its root.
+
+ """
+ # The pattern can't end with a wildcard component.
+ pattern = pattern.rstrip(r"\/")
+ if pattern.endswith("*"):
+ raise CoverageException("Pattern must not end with wildcards.")
+ pattern_sep = sep(pattern)
+
+ # The pattern is meant to match a filepath. Let's make it absolute
+ # unless it already is, or is meant to match any prefix.
+ if not pattern.startswith('*') and not isabs_anywhere(pattern):
+ pattern = abs_file(pattern)
+ pattern += pattern_sep
+
+ # Make a regex from the pattern. fnmatch always adds a \Z or $ to
+ # match the whole string, which we don't want.
+ regex_pat = fnmatch.translate(pattern).replace(r'\Z(', '(')
+ if regex_pat.endswith("$"):
+ regex_pat = regex_pat[:-1]
+ # We want */a/b.py to match on Windows too, so change slash to match
+ # either separator.
+ regex_pat = regex_pat.replace(r"\/", r"[\\/]")
+ # We want case-insensitive matching, so add that flag.
+ regex = re.compile(r"(?i)" + regex_pat)
+
+ # Normalize the result: it must end with a path separator.
+ result_sep = sep(result)
+ result = result.rstrip(r"\/") + result_sep
+ self.aliases.append((regex, result, pattern_sep, result_sep))
+
+ def map(self, path):
+ """Map `path` through the aliases.
+
+ `path` is checked against all of the patterns. The first pattern to
+ match is used to replace the root of the path with the result root.
+ Only one pattern is ever used. If no patterns match, `path` is
+ returned unchanged.
+
+ The separator style in the result is made to match that of the result
+ in the alias.
+
+ """
+ for regex, result, pattern_sep, result_sep in self.aliases:
+ m = regex.match(path)
+ if m:
+ new = path.replace(m.group(0), result)
+ if pattern_sep != result_sep:
+ new = new.replace(pattern_sep, result_sep)
+ if self.locator:
+ new = self.locator.canonical_filename(new)
+ return new
+ return path
+
+
def find_python_files(dirname):
- """Yield all of the importable Python files in `dirname`, recursively."""
- for dirpath, dirnames, filenames in os.walk(dirname, topdown=True):
- if '__init__.py' not in filenames:
+ """Yield all of the importable Python files in `dirname`, recursively.
+
+ To be importable, the files have to be in a directory with a __init__.py,
+ except for `dirname` itself, which isn't required to have one. The
+ assumption is that `dirname` was specified directly, so the user knows
+ best, but subdirectories are checked for a __init__.py to be sure we only
+ find the importable files.
+
+ """
+ for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)):
+ if i > 0 and '__init__.py' not in filenames:
# If a directory doesn't have __init__.py, then it isn't
# importable and neither are its files
del dirnames[:]
continue
for filename in filenames:
- if fnmatch.fnmatch(filename, "*.py"):
+ # We're only interested in files that look like reasonable Python
+ # files: Must end with .py or .pyw, and must not have certain funny
+ # characters that probably mean they are editor junk.
+ if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename):
yield os.path.join(dirpath, filename)
diff --git a/python/helpers/coverage/fullcoverage/encodings.py b/python/helpers/coverage/fullcoverage/encodings.py
new file mode 100644
index 0000000..6a258d6
--- /dev/null
+++ b/python/helpers/coverage/fullcoverage/encodings.py
@@ -0,0 +1,57 @@
+"""Imposter encodings module that installs a coverage-style tracer.
+
+This is NOT the encodings module; it is an imposter that sets up tracing
+instrumentation and then replaces itself with the real encodings module.
+
+If the directory that holds this file is placed first in the PYTHONPATH when
+using "coverage" to run Python's tests, then this file will become the very
+first module imported by the internals of Python 3. It installs a
+coverage-compatible trace function that can watch Standard Library modules
+execute from the very earliest stages of Python's own boot process. This fixes
+a problem with coverage - that it starts too late to trace the coverage of many
+of the most fundamental modules in the Standard Library.
+
+"""
+
+import sys
+
+class FullCoverageTracer(object):
+ def __init__(self):
+ # `traces` is a list of trace events. Frames are tricky: the same
+ # frame object is used for a whole scope, with new line numbers
+ # written into it. So in one scope, all the frame objects are the
+ # same object, and will eventually all will point to the last line
+ # executed. So we keep the line numbers alongside the frames.
+ # The list looks like:
+ #
+ # traces = [
+ # ((frame, event, arg), lineno), ...
+ # ]
+ #
+ self.traces = []
+
+ def fullcoverage_trace(self, *args):
+ frame, event, arg = args
+ self.traces.append((args, frame.f_lineno))
+ return self.fullcoverage_trace
+
+sys.settrace(FullCoverageTracer().fullcoverage_trace)
+
+# In coverage/files.py is actual_filename(), which uses glob.glob. I don't
+# understand why, but that use of glob borks everything if fullcoverage is in
+# effect. So here we make an ugly hail-mary pass to switch off glob.glob over
+# there. This means when using fullcoverage, Windows path names will not be
+# their actual case.
+
+#sys.fullcoverage = True
+
+# Finally, remove our own directory from sys.path; remove ourselves from
+# sys.modules; and re-import "encodings", which will be the real package
+# this time. Note that the delete from sys.modules dictionary has to
+# happen last, since all of the symbols in this module will become None
+# at that exact moment, including "sys".
+
+parentdir = max(filter(__file__.startswith, sys.path), key=len)
+sys.path.remove(parentdir)
+del sys.modules['encodings']
+import encodings
diff --git a/python/helpers/coverage/html.py b/python/helpers/coverage/html.py
index fffd9b4..5242236 100644
--- a/python/helpers/coverage/html.py
+++ b/python/helpers/coverage/html.py
@@ -1,21 +1,45 @@
"""HTML reporting for Coverage."""
-import os, re, shutil
+import os, re, shutil, sys
import coverage
from coverage.backward import pickle
from coverage.misc import CoverageException, Hasher
-from coverage.phystokens import source_token_lines
+from coverage.phystokens import source_token_lines, source_encoding
from coverage.report import Reporter
+from coverage.results import Numbers
from coverage.templite import Templite
-# Disable pylint msg W0612, because a bunch of variables look unused, but
-# they're accessed in a Templite context via locals().
-# pylint: disable=W0612
-def data_filename(fname):
- """Return the path to a data file of ours."""
- return os.path.join(os.path.split(__file__)[0], fname)
+# Static files are looked for in a list of places.
+STATIC_PATH = [
+ # The place Debian puts system Javascript libraries.
+ "/usr/share/javascript",
+
+ # Our htmlfiles directory.
+ os.path.join(os.path.dirname(__file__), "htmlfiles"),
+]
+
+def data_filename(fname, pkgdir=""):
+ """Return the path to a data file of ours.
+
+ The file is searched for on `STATIC_PATH`, and the first place it's found,
+ is returned.
+
+ Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
+ is provided, at that subdirectory.
+
+ """
+ for static_dir in STATIC_PATH:
+ static_filename = os.path.join(static_dir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ if pkgdir:
+ static_filename = os.path.join(static_dir, pkgdir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ raise CoverageException("Couldn't find static file %r" % fname)
+
def data(fname):
"""Return the contents of a data file of ours."""
@@ -31,26 +55,27 @@
# These files will be copied from the htmlfiles dir to the output dir.
STATIC_FILES = [
- "style.css",
- "jquery-1.4.3.min.js",
- "jquery.hotkeys.js",
- "jquery.isonscreen.js",
- "jquery.tablesorter.min.js",
- "coverage_html.js",
- "keybd_closed.png",
- "keybd_open.png",
+ ("style.css", ""),
+ ("jquery.min.js", "jquery"),
+ ("jquery.hotkeys.js", "jquery-hotkeys"),
+ ("jquery.isonscreen.js", "jquery-isonscreen"),
+ ("jquery.tablesorter.min.js", "jquery-tablesorter"),
+ ("coverage_html.js", ""),
+ ("keybd_closed.png", ""),
+ ("keybd_open.png", ""),
]
- def __init__(self, cov, ignore_errors=False):
- super(HtmlReporter, self).__init__(cov, ignore_errors)
+ def __init__(self, cov, config):
+ super(HtmlReporter, self).__init__(cov, config)
self.directory = None
self.template_globals = {
'escape': escape,
+ 'title': self.config.html_title,
'__url__': coverage.__url__,
'__version__': coverage.__version__,
}
self.source_tmpl = Templite(
- data("htmlfiles/pyfile.html"), self.template_globals
+ data("pyfile.html"), self.template_globals
)
self.coverage = cov
@@ -58,29 +83,34 @@
self.files = []
self.arcs = self.coverage.data.has_arcs()
self.status = HtmlStatus()
+ self.extra_css = None
+ self.totals = Numbers()
- def report(self, morfs, config=None):
+ def report(self, morfs):
"""Generate an HTML report for `morfs`.
- `morfs` is a list of modules or filenames. `config` is a
- CoverageConfig instance.
+ `morfs` is a list of modules or filenames.
"""
- assert config.html_dir, "must provide a directory for html reporting"
+ assert self.config.html_dir, "must give a directory for html reporting"
# Read the status data.
- self.status.read(config.html_dir)
+ self.status.read(self.config.html_dir)
# Check that this run used the same settings as the last run.
m = Hasher()
- m.update(config)
+ m.update(self.config)
these_settings = m.digest()
if self.status.settings_hash() != these_settings:
self.status.reset()
self.status.set_settings_hash(these_settings)
+ # The user may have extra CSS they want copied.
+ if self.config.extra_css:
+ self.extra_css = os.path.basename(self.config.extra_css)
+
# Process all the files.
- self.report_files(self.html_file, morfs, config, config.html_dir)
+ self.report_files(self.html_file, morfs, self.config.html_dir)
if not self.files:
raise CoverageException("No data to report.")
@@ -88,13 +118,34 @@
# Write the index file.
self.index_file()
- # Create the once-per-directory files.
- for static in self.STATIC_FILES:
+ self.make_local_static_report_files()
+
+ return self.totals.pc_covered
+
+ def make_local_static_report_files(self):
+ """Make local instances of static files for HTML report."""
+ # The files we provide must always be copied.
+ for static, pkgdir in self.STATIC_FILES:
shutil.copyfile(
- data_filename("htmlfiles/" + static),
+ data_filename(static, pkgdir),
os.path.join(self.directory, static)
)
+ # The user may have extra CSS they want copied.
+ if self.extra_css:
+ shutil.copyfile(
+ self.config.extra_css,
+ os.path.join(self.directory, self.extra_css)
+ )
+
+ def write_html(self, fname, html):
+ """Write `html` to `fname`, properly encoded."""
+ fout = open(fname, "wb")
+ try:
+ fout.write(html.encode('ascii', 'xmlcharrefreplace'))
+ finally:
+ fout.close()
+
def file_hash(self, source, cu):
"""Compute a hash that changes if the file needs to be re-reported."""
m = Hasher()
@@ -121,11 +172,20 @@
self.status.set_file_hash(flat_rootname, this_hash)
+ # If need be, determine the encoding of the source file. We use it
+ # later to properly write the HTML.
+ if sys.version_info < (3, 0):
+ encoding = source_encoding(source)
+ # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
+ if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
+ source = source[3:]
+ encoding = "utf-8"
+
+ # Get the numbers for this file.
nums = analysis.numbers
- missing_branch_arcs = analysis.missing_branch_arcs()
- n_par = 0 # accumulated below.
- arcs = self.arcs
+ if self.arcs:
+ missing_branch_arcs = analysis.missing_branch_arcs()
# These classes determine which lines are highlighted by default.
c_run = "run hide_run"
@@ -149,7 +209,6 @@
line_class.append(c_mis)
elif self.arcs and lineno in missing_branch_arcs:
line_class.append(c_par)
- n_par += 1
annlines = []
for b in missing_branch_arcs[lineno]:
if b < 0:
@@ -184,19 +243,22 @@
})
# Write the HTML page for this file.
+ html = spaceless(self.source_tmpl.render({
+ 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
+ 'arcs': self.arcs, 'extra_css': self.extra_css,
+ 'cu': cu, 'nums': nums, 'lines': lines,
+ }))
+
+ if sys.version_info < (3, 0):
+ html = html.decode(encoding)
+
html_filename = flat_rootname + ".html"
html_path = os.path.join(self.directory, html_filename)
- html = spaceless(self.source_tmpl.render(locals()))
- fhtml = open(html_path, 'w')
- try:
- fhtml.write(html)
- finally:
- fhtml.close()
+ self.write_html(html_path, html)
# Save this file's information for the index file.
index_info = {
'nums': nums,
- 'par': n_par,
'html_filename': html_filename,
'name': cu.name,
}
@@ -206,19 +268,24 @@
def index_file(self):
"""Write the index.html file for this report."""
index_tmpl = Templite(
- data("htmlfiles/index.html"), self.template_globals
+ data("index.html"), self.template_globals
)
- files = self.files
- arcs = self.arcs
+ self.totals = sum([f['nums'] for f in self.files])
- totals = sum([f['nums'] for f in files])
+ html = index_tmpl.render({
+ 'arcs': self.arcs,
+ 'extra_css': self.extra_css,
+ 'files': self.files,
+ 'totals': self.totals,
+ })
- fhtml = open(os.path.join(self.directory, "index.html"), "w")
- try:
- fhtml.write(index_tmpl.render(locals()))
- finally:
- fhtml.close()
+ if sys.version_info < (3, 0):
+ html = html.decode("utf-8")
+ self.write_html(
+ os.path.join(self.directory, "index.html"),
+ html
+ )
# Write the latest hashes for next time.
self.status.write(self.directory)
@@ -243,8 +310,12 @@
usable = False
try:
status_file = os.path.join(directory, self.STATUS_FILE)
- status = pickle.load(open(status_file, "rb"))
- except IOError:
+ fstatus = open(status_file, "rb")
+ try:
+ status = pickle.load(fstatus)
+ finally:
+ fstatus.close()
+ except (IOError, ValueError):
usable = False
else:
usable = True
@@ -321,5 +392,5 @@
Get rid of some.
"""
- html = re.sub(">\s+<p ", ">\n<p ", html)
+ html = re.sub(r">\s+<p ", ">\n<p ", html)
return html
diff --git a/python/helpers/coverage/htmlfiles/coverage_html.js b/python/helpers/coverage/htmlfiles/coverage_html.js
index da3e22c..b24006d 100644
--- a/python/helpers/coverage/htmlfiles/coverage_html.js
+++ b/python/helpers/coverage/htmlfiles/coverage_html.js
@@ -122,6 +122,11 @@
.bind('keydown', '1', coverage.to_first_chunk)
;
+ $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
+ $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
+ $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
+ $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
+
coverage.assign_shortkeys();
coverage.wire_up_help_panel();
};
@@ -369,4 +374,3 @@
coverage.finish_scrolling = function () {
$("html,body").stop(true, true);
};
-
diff --git a/python/helpers/coverage/htmlfiles/index.html b/python/helpers/coverage/htmlfiles/index.html
index 04b314a..c831823 100644
--- a/python/helpers/coverage/htmlfiles/index.html
+++ b/python/helpers/coverage/htmlfiles/index.html
@@ -2,9 +2,12 @@
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
- <title>Coverage report</title>
+ <title>{{ title|escape }}</title>
<link rel='stylesheet' href='style.css' type='text/css'>
- <script type='text/javascript' src='jquery-1.4.3.min.js'></script>
+ {% if extra_css %}
+ <link rel='stylesheet' href='{{ extra_css }}' type='text/css'>
+ {% endif %}
+ <script type='text/javascript' src='jquery.min.js'></script>
<script type='text/javascript' src='jquery.tablesorter.min.js'></script>
<script type='text/javascript' src='jquery.hotkeys.js'></script>
<script type='text/javascript' src='coverage_html.js'></script>
@@ -16,7 +19,7 @@
<div id='header'>
<div class='content'>
- <h1>Coverage report:
+ <h1>{{ title|escape }}:
<span class='pc_cov'>{{totals.pc_covered_str}}%</span>
</h1>
<img id='keyboard_icon' src='keybd_closed.png'>
@@ -66,7 +69,7 @@
<td>{{totals.n_excluded}}</td>
{% if arcs %}
<td>{{totals.n_branches}}</td>
- <td>{{totals.n_missing_branches}}</td>
+ <td>{{totals.n_partial_branches}}</td>
{% endif %}
<td class='right'>{{totals.pc_covered_str}}%</td>
</tr>
@@ -80,7 +83,7 @@
<td>{{file.nums.n_excluded}}</td>
{% if arcs %}
<td>{{file.nums.n_branches}}</td>
- <td>{{file.nums.n_missing_branches}}</td>
+ <td>{{file.nums.n_partial_branches}}</td>
{% endif %}
<td class='right'>{{file.nums.pc_covered_str}}%</td>
</tr>
diff --git a/python/helpers/coverage/htmlfiles/jquery-1.4.3.min.js b/python/helpers/coverage/htmlfiles/jquery.min.js
similarity index 100%
rename from python/helpers/coverage/htmlfiles/jquery-1.4.3.min.js
rename to python/helpers/coverage/htmlfiles/jquery.min.js
diff --git a/python/helpers/coverage/htmlfiles/keybd_closed.png b/python/helpers/coverage/htmlfiles/keybd_closed.png
old mode 100644
new mode 100755
index 6843abf..f2b0418
--- a/python/helpers/coverage/htmlfiles/keybd_closed.png
+++ b/python/helpers/coverage/htmlfiles/keybd_closed.png
Binary files differ
diff --git a/python/helpers/coverage/htmlfiles/keybd_open.png b/python/helpers/coverage/htmlfiles/keybd_open.png
old mode 100644
new mode 100755
index 5a681ea..a77961d
--- a/python/helpers/coverage/htmlfiles/keybd_open.png
+++ b/python/helpers/coverage/htmlfiles/keybd_open.png
Binary files differ
diff --git a/python/helpers/coverage/htmlfiles/pyfile.html b/python/helpers/coverage/htmlfiles/pyfile.html
index ee0a3b1..88c158d 100644
--- a/python/helpers/coverage/htmlfiles/pyfile.html
+++ b/python/helpers/coverage/htmlfiles/pyfile.html
@@ -7,7 +7,10 @@
<meta http-equiv='X-UA-Compatible' content='IE=emulateIE7' />
<title>Coverage for {{cu.name|escape}}: {{nums.pc_covered_str}}%</title>
<link rel='stylesheet' href='style.css' type='text/css'>
- <script type='text/javascript' src='jquery-1.4.3.min.js'></script>
+ {% if extra_css %}
+ <link rel='stylesheet' href='{{ extra_css }}' type='text/css'>
+ {% endif %}
+ <script type='text/javascript' src='jquery.min.js'></script>
<script type='text/javascript' src='jquery.hotkeys.js'></script>
<script type='text/javascript' src='jquery.isonscreen.js'></script>
<script type='text/javascript' src='coverage_html.js'></script>
@@ -24,12 +27,12 @@
</h1>
<img id='keyboard_icon' src='keybd_closed.png'>
<h2 class='stats'>
- {{nums.n_statements}} statements
- <span class='{{c_run}} shortkey_r' onclick='coverage.toggle_lines(this, "run")'>{{nums.n_executed}} run</span>
- <span class='{{c_mis}} shortkey_m' onclick='coverage.toggle_lines(this, "mis")'>{{nums.n_missing}} missing</span>
- <span class='{{c_exc}} shortkey_x' onclick='coverage.toggle_lines(this, "exc")'>{{nums.n_excluded}} excluded</span>
+ {{nums.n_statements}} statements
+ <span class='{{c_run}} shortkey_r button_toggle_run'>{{nums.n_executed}} run</span>
+ <span class='{{c_mis}} shortkey_m button_toggle_mis'>{{nums.n_missing}} missing</span>
+ <span class='{{c_exc}} shortkey_x button_toggle_exc'>{{nums.n_excluded}} excluded</span>
{% if arcs %}
- <span class='{{c_par}} shortkey_p' onclick='coverage.toggle_lines(this, "par")'>{{n_par}} partial</span>
+ <span class='{{c_par}} shortkey_p button_toggle_par'>{{nums.n_partial_branches}} partial</span>
{% endif %}
</h2>
</div>
diff --git a/python/helpers/coverage/htmlfiles/style.css b/python/helpers/coverage/htmlfiles/style.css
index c40357b..811c640 100644
--- a/python/helpers/coverage/htmlfiles/style.css
+++ b/python/helpers/coverage/htmlfiles/style.css
@@ -24,8 +24,8 @@
/* Set base font size to 12/16 */
p {
- font-size: .75em; /* 12/16 */
- line-height: 1.3333em; /* 16/12 */
+ font-size: .75em; /* 12/16 */
+ line-height: 1.33333333em; /* 16/12 */
}
table {
@@ -102,6 +102,31 @@
border-color: #999 #ccc #ccc #999;
}
+.stats span.run {
+ background: #ddffdd;
+}
+.stats span.exc {
+ background: #eeeeee;
+}
+.stats span.mis {
+ background: #ffdddd;
+}
+.stats span.hide_run {
+ background: #eeffee;
+}
+.stats span.hide_exc {
+ background: #f5f5f5;
+}
+.stats span.hide_mis {
+ background: #ffeeee;
+}
+.stats span.par {
+ background: #ffffaa;
+}
+.stats span.hide_par {
+ background: #ffffcc;
+}
+
/* Help panel */
#keyboard_icon {
float: right;
diff --git a/python/helpers/coverage/misc.py b/python/helpers/coverage/misc.py
index fd9be85..0378173 100644
--- a/python/helpers/coverage/misc.py
+++ b/python/helpers/coverage/misc.py
@@ -1,6 +1,10 @@
"""Miscellaneous stuff for Coverage."""
+import errno
import inspect
+import os
+import sys
+
from coverage.backward import md5, sorted # pylint: disable=W0622
from coverage.backward import string_class, to_bytes
@@ -34,6 +38,8 @@
i = 0
j = 0
start = None
+ statements = sorted(statements)
+ lines = sorted(lines)
while i < len(statements) and j < len(lines):
if statements[i] == lines[j]:
if start == None:
@@ -50,6 +56,12 @@
return ret
+def short_stack():
+ """Return a string summarizing the call stack."""
+ stack = inspect.stack()[:0:-1]
+ return "\n".join(["%30s : %s @%d" % (t[3],t[1],t[2]) for t in stack])
+
+
def expensive(fn):
"""A decorator to cache the result of an expensive operation.
@@ -76,13 +88,23 @@
def join_regex(regexes):
"""Combine a list of regexes into one that matches any of them."""
if len(regexes) > 1:
- return "(" + ")|(".join(regexes) + ")"
+ return "|".join(["(%s)" % r for r in regexes])
elif regexes:
return regexes[0]
else:
return ""
+def file_be_gone(path):
+ """Remove a file, and don't get annoyed if it doesn't exist."""
+ try:
+ os.remove(path)
+ except OSError:
+ _, e, _ = sys.exc_info()
+ if e.errno != errno.ENOENT:
+ raise
+
+
class Hasher(object):
"""Hashes Python data into md5."""
def __init__(self):
@@ -93,8 +115,10 @@
self.md5.update(to_bytes(str(type(v))))
if isinstance(v, string_class):
self.md5.update(to_bytes(v))
+ elif v is None:
+ pass
elif isinstance(v, (int, float)):
- self.update(str(v))
+ self.md5.update(to_bytes(str(v)))
elif isinstance(v, (tuple, list)):
for e in v:
self.update(e)
@@ -126,6 +150,10 @@
"""We couldn't find the source for a module."""
pass
+class NoCode(NoSource):
+ """We couldn't find any code at all."""
+ pass
+
class NotPython(CoverageException):
"""A source file turned out not to be parsable Python."""
pass
diff --git a/python/helpers/coverage/parser.py b/python/helpers/coverage/parser.py
index cbbb5a6..7a145a2 100644
--- a/python/helpers/coverage/parser.py
+++ b/python/helpers/coverage/parser.py
@@ -1,9 +1,11 @@
"""Code parsing for Coverage."""
-import glob, opcode, os, re, sys, token, tokenize
+import dis, re, sys, token, tokenize
from coverage.backward import set, sorted, StringIO # pylint: disable=W0622
-from coverage.backward import open_source
+from coverage.backward import open_source, range # pylint: disable=W0622
+from coverage.backward import reversed # pylint: disable=W0622
+from coverage.backward import bytes_to_ints
from coverage.bytecode import ByteCodes, CodeObjects
from coverage.misc import nice_pair, expensive, join_regex
from coverage.misc import CoverageException, NoSource, NotPython
@@ -32,9 +34,13 @@
except IOError:
_, err, _ = sys.exc_info()
raise NoSource(
- "No source for code: %r: %s" % (self.filename, err)
+ "No source for code: '%s': %s" % (self.filename, err)
)
+ # Scrap the BOM if it exists.
+ if self.text and ord(self.text[0]) == 0xfeff:
+ self.text = self.text[1:]
+
self.exclude = exclude
self.show_tokens = False
@@ -102,9 +108,9 @@
first_line = None
empty = True
- tokgen = tokenize.generate_tokens(StringIO(self.text).readline)
+ tokgen = generate_tokens(self.text)
for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
- if self.show_tokens: # pragma: no cover
+ if self.show_tokens: # pragma: not covered
print("%10s %5s %-20r %r" % (
tokenize.tok_name.get(toktype, toktype),
nice_pair((slineno, elineno)), ttext, ltext
@@ -130,8 +136,7 @@
# (a trick from trace.py in the stdlib.) This works for
# 99.9999% of cases. For the rest (!) see:
# http://stackoverflow.com/questions/1769332/x/1769794#1769794
- for i in range(slineno, elineno+1):
- self.docstrings.add(i)
+ self.docstrings.update(range(slineno, elineno+1))
elif toktype == token.NEWLINE:
if first_line is not None and elineno != first_line:
# We're at the end of a line, and we've ended on a
@@ -170,16 +175,18 @@
first_line = line
return first_line
- def first_lines(self, lines, ignore=None):
+ def first_lines(self, lines, *ignores):
"""Map the line numbers in `lines` to the correct first line of the
statement.
- Skip any line mentioned in `ignore`.
+ Skip any line mentioned in any of the sequences in `ignores`.
- Returns a sorted list of the first lines.
+ Returns a set of the first lines.
"""
- ignore = ignore or []
+ ignore = set()
+ for ign in ignores:
+ ignore.update(ign)
lset = set()
for l in lines:
if l in ignore:
@@ -187,23 +194,34 @@
new_l = self.first_line(l)
if new_l not in ignore:
lset.add(new_l)
- return sorted(lset)
+ return lset
def parse_source(self):
"""Parse source text to find executable lines, excluded lines, etc.
- Return values are 1) a sorted list of executable line numbers, and
- 2) a sorted list of excluded line numbers.
+ Return values are 1) a set of executable line numbers, and 2) a set of
+ excluded line numbers.
Reported line numbers are normalized to the first line of multi-line
statements.
"""
- self._raw_parse()
+ try:
+ self._raw_parse()
+ except (tokenize.TokenError, IndentationError):
+ _, tokerr, _ = sys.exc_info()
+ msg, lineno = tokerr.args
+ raise NotPython(
+ "Couldn't parse '%s' as Python source: '%s' at %s" %
+ (self.filename, msg, lineno)
+ )
excluded_lines = self.first_lines(self.excluded)
- ignore = excluded_lines + list(self.docstrings)
- lines = self.first_lines(self.statement_starts, ignore)
+ lines = self.first_lines(
+ self.statement_starts,
+ excluded_lines,
+ self.docstrings
+ )
return lines, excluded_lines
@@ -258,8 +276,8 @@
## Opcodes that guide the ByteParser.
def _opcode(name):
- """Return the opcode by name from the opcode module."""
- return opcode.opmap[name]
+ """Return the opcode by name from the dis module."""
+ return dis.opmap[name]
def _opcode_set(*names):
"""Return a set of opcodes by the names in `names`."""
@@ -297,7 +315,7 @@
OPS_POP_BLOCK = _opcode_set('POP_BLOCK')
# Opcodes that have a jump destination, but aren't really a jump.
-OPS_NO_JUMP = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY')
+OPS_NO_JUMP = OPS_PUSH_BLOCK
# Individual opcodes we need below.
OP_BREAK_LOOP = _opcode('BREAK_LOOP')
@@ -314,6 +332,7 @@
def __init__(self, code=None, text=None, filename=None):
if code:
self.code = code
+ self.text = text
else:
if not text:
assert filename, "If no code or text, need a filename"
@@ -322,6 +341,7 @@
text = sourcef.read()
finally:
sourcef.close()
+ self.text = text
try:
# Python 2.3 and 2.4 don't like partial last lines, so be sure
@@ -350,69 +370,54 @@
The iteration includes `self` as its first value.
"""
- return map(lambda c: ByteParser(code=c), CodeObjects(self.code))
-
- # Getting numbers from the lnotab value changed in Py3.0.
- if sys.version_info >= (3, 0):
- def _lnotab_increments(self, lnotab):
- """Return a list of ints from the lnotab bytes in 3.x"""
- return list(lnotab)
- else:
- def _lnotab_increments(self, lnotab):
- """Return a list of ints from the lnotab string in 2.x"""
- return [ord(c) for c in lnotab]
+ children = CodeObjects(self.code)
+ return [ByteParser(code=c, text=self.text) for c in children]
def _bytes_lines(self):
"""Map byte offsets to line numbers in `code`.
Uses co_lnotab described in Python/compile.c to map byte offsets to
- line numbers. Returns a list: [(b0, l0), (b1, l1), ...]
+ line numbers. Produces a sequence: (b0, l0), (b1, l1), ...
+
+ Only byte offsets that correspond to line numbers are included in the
+ results.
"""
# Adapted from dis.py in the standard library.
- byte_increments = self._lnotab_increments(self.code.co_lnotab[0::2])
- line_increments = self._lnotab_increments(self.code.co_lnotab[1::2])
+ byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+ line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
- bytes_lines = []
last_line_num = None
line_num = self.code.co_firstlineno
byte_num = 0
for byte_incr, line_incr in zip(byte_increments, line_increments):
if byte_incr:
if line_num != last_line_num:
- bytes_lines.append((byte_num, line_num))
+ yield (byte_num, line_num)
last_line_num = line_num
byte_num += byte_incr
line_num += line_incr
if line_num != last_line_num:
- bytes_lines.append((byte_num, line_num))
- return bytes_lines
+ yield (byte_num, line_num)
def _find_statements(self):
"""Find the statements in `self.code`.
- Return a set of line numbers that start statements. Recurses into all
- code objects reachable from `self.code`.
+ Produce a sequence of line numbers that start statements. Recurses
+ into all code objects reachable from `self.code`.
"""
- stmts = set()
for bp in self.child_parsers():
# Get all of the lineno information from this code.
for _, l in bp._bytes_lines():
- stmts.add(l)
- return stmts
+ yield l
- def _disassemble(self): # pragma: no cover
- """Disassemble code, for ad-hoc experimenting."""
-
- import dis
-
- for bp in self.child_parsers():
- print("\n%s: " % bp.code)
- dis.dis(bp.code)
- print("Bytes lines: %r" % bp._bytes_lines())
-
- print("")
+ def _block_stack_repr(self, block_stack):
+ """Get a string version of `block_stack`, for debugging."""
+ blocks = ", ".join(
+ ["(%s, %r)" % (dis.opname[b[0]], b[1]) for b in block_stack]
+ )
+ return "[" + blocks + "]"
def _split_into_chunks(self):
"""Split the code object into a list of `Chunk` objects.
@@ -423,10 +428,11 @@
Returns a list of `Chunk` objects.
"""
-
# The list of chunks so far, and the one we're working on.
chunks = []
chunk = None
+
+ # A dict mapping byte offsets of line starts to the line numbers.
bytes_lines_map = dict(self._bytes_lines())
# The block stack: loops and try blocks get pushed here for the
@@ -441,24 +447,38 @@
# We have to handle the last two bytecodes specially.
ult = penult = None
- for bc in ByteCodes(self.code.co_code):
+ # Get a set of all of the jump-to points.
+ jump_to = set()
+ bytecodes = list(ByteCodes(self.code.co_code))
+ for bc in bytecodes:
+ if bc.jump_to >= 0:
+ jump_to.add(bc.jump_to)
+
+ chunk_lineno = 0
+
+ # Walk the byte codes building chunks.
+ for bc in bytecodes:
# Maybe have to start a new chunk
+ start_new_chunk = False
+ first_chunk = False
if bc.offset in bytes_lines_map:
# Start a new chunk for each source line number.
- if chunk:
- chunk.exits.add(bc.offset)
- chunk = Chunk(bc.offset, bytes_lines_map[bc.offset])
- chunks.append(chunk)
+ start_new_chunk = True
+ chunk_lineno = bytes_lines_map[bc.offset]
+ first_chunk = True
+ elif bc.offset in jump_to:
+ # To make chunks have a single entrance, we have to make a new
+ # chunk when we get to a place some bytecode jumps to.
+ start_new_chunk = True
elif bc.op in OPS_CHUNK_BEGIN:
# Jumps deserve their own unnumbered chunk. This fixes
# problems with jumps to jumps getting confused.
+ start_new_chunk = True
+
+ if not chunk or start_new_chunk:
if chunk:
chunk.exits.add(bc.offset)
- chunk = Chunk(bc.offset)
- chunks.append(chunk)
-
- if not chunk:
- chunk = Chunk(bc.offset)
+ chunk = Chunk(bc.offset, chunk_lineno, first_chunk)
chunks.append(chunk)
# Look at the opcode
@@ -487,15 +507,11 @@
chunk.exits.add(block_stack[-1][1])
chunk = None
if bc.op == OP_END_FINALLY:
- if block_stack:
- # A break that goes through a finally will jump to whatever
- # block is on top of the stack.
- chunk.exits.add(block_stack[-1][1])
# For the finally clause we need to find the closest exception
# block, and use its jump target as an exit.
- for iblock in range(len(block_stack)-1, -1, -1):
- if block_stack[iblock][0] in OPS_EXCEPT_BLOCKS:
- chunk.exits.add(block_stack[iblock][1])
+ for block in reversed(block_stack):
+ if block[0] in OPS_EXCEPT_BLOCKS:
+ chunk.exits.add(block[1])
break
if bc.op == OP_COMPARE_OP and bc.arg == COMPARE_EXCEPTION:
# This is an except clause. We want to overlook the next
@@ -521,23 +537,33 @@
last_chunk = chunks[-1]
last_chunk.exits.remove(ex)
last_chunk.exits.add(penult.offset)
- chunk = Chunk(penult.offset)
+ chunk = Chunk(
+ penult.offset, last_chunk.line, False
+ )
chunk.exits.add(ex)
chunks.append(chunk)
# Give all the chunks a length.
- chunks[-1].length = bc.next_offset - chunks[-1].byte
+ chunks[-1].length = bc.next_offset - chunks[-1].byte # pylint: disable=W0631,C0301
for i in range(len(chunks)-1):
chunks[i].length = chunks[i+1].byte - chunks[i].byte
+ #self.validate_chunks(chunks)
return chunks
+ def validate_chunks(self, chunks):
+ """Validate the rule that chunks have a single entrance."""
+ # starts is the entrances to the chunks
+ starts = set([ch.byte for ch in chunks])
+ for ch in chunks:
+ assert all([(ex in starts or ex < 0) for ex in ch.exits])
+
def _arcs(self):
"""Find the executable arcs in the code.
- Returns a set of pairs, (from,to). From and to are integer line
- numbers. If from is < 0, then the arc is an entrance into the code
- object. If to is < 0, the arc is an exit from the code object.
+ Yields pairs: (from,to). From and to are integer line numbers. If
+ from is < 0, then the arc is an entrance into the code object. If to
+ is < 0, the arc is an exit from the code object.
"""
chunks = self._split_into_chunks()
@@ -545,65 +571,43 @@
# A map from byte offsets to chunks jumped into.
byte_chunks = dict([(c.byte, c) for c in chunks])
- # Build a map from byte offsets to actual lines reached.
- byte_lines = {}
- bytes_to_add = set([c.byte for c in chunks])
+ # There's always an entrance at the first chunk.
+ yield (-1, byte_chunks[0].line)
- while bytes_to_add:
- byte_to_add = bytes_to_add.pop()
- if byte_to_add in byte_lines or byte_to_add < 0:
+ # Traverse from the first chunk in each line, and yield arcs where
+ # the trace function will be invoked.
+ for chunk in chunks:
+ if not chunk.first:
continue
- # Which lines does this chunk lead to?
- bytes_considered = set()
- bytes_to_consider = [byte_to_add]
- lines = set()
+ chunks_considered = set()
+ chunks_to_consider = [chunk]
+ while chunks_to_consider:
+ # Get the chunk we're considering, and make sure we don't
+ # consider it again
+ this_chunk = chunks_to_consider.pop()
+ chunks_considered.add(this_chunk)
- while bytes_to_consider:
- byte = bytes_to_consider.pop()
- bytes_considered.add(byte)
-
- # Find chunk for byte
- try:
- ch = byte_chunks[byte]
- except KeyError:
- for ch in chunks:
- if ch.byte <= byte < ch.byte+ch.length:
- break
- else:
- # No chunk for this byte!
- raise Exception("Couldn't find chunk @ %d" % byte)
- byte_chunks[byte] = ch
-
- if ch.line:
- lines.add(ch.line)
- else:
- for ex in ch.exits:
- if ex < 0:
- lines.add(ex)
- elif ex not in bytes_considered:
- bytes_to_consider.append(ex)
-
- bytes_to_add.update(ch.exits)
-
- byte_lines[byte_to_add] = lines
-
- # Figure out for each chunk where the exits go.
- arcs = set()
- for chunk in chunks:
- if chunk.line:
- for ex in chunk.exits:
+ # For each exit, add the line number if the trace function
+ # would be triggered, or add the chunk to those being
+ # considered if not.
+ for ex in this_chunk.exits:
if ex < 0:
- exit_lines = [ex]
+ yield (chunk.line, ex)
else:
- exit_lines = byte_lines[ex]
- for exit_line in exit_lines:
- if chunk.line != exit_line:
- arcs.add((chunk.line, exit_line))
- for line in byte_lines[0]:
- arcs.add((-1, line))
+ next_chunk = byte_chunks[ex]
+ if next_chunk in chunks_considered:
+ continue
- return arcs
+ # The trace function is invoked if visiting the first
+ # bytecode in a line, or if the transition is a
+ # backward jump.
+ backward_jump = next_chunk.byte < this_chunk.byte
+ if next_chunk.first or backward_jump:
+ if next_chunk.line != chunk.line:
+ yield (chunk.line, next_chunk.line)
+ else:
+ chunks_to_consider.append(next_chunk)
def _all_chunks(self):
"""Returns a list of `Chunk` objects for this code and its children.
@@ -631,11 +635,11 @@
class Chunk(object):
- """A sequence of bytecodes with a single entrance.
+ """A sequence of byte codes with a single entrance.
To analyze byte code, we have to divide it into chunks, sequences of byte
- codes such that each basic block has only one entrance, the first
- instruction in the block.
+ codes such that each chunk has only one entrance, the first instruction in
+ the block.
This is almost the CS concept of `basic block`_, except that we're willing
to have many exits from a chunk, and "basic block" is a more cumbersome
@@ -643,158 +647,54 @@
.. _basic block: http://en.wikipedia.org/wiki/Basic_block
+ `line` is the source line number containing this chunk.
+
+ `first` is true if this is the first chunk in the source line.
+
An exit < 0 means the chunk can leave the code (return). The exit is
the negative of the starting line number of the code block.
"""
- def __init__(self, byte, line=0):
+ def __init__(self, byte, line, first):
self.byte = byte
self.line = line
+ self.first = first
self.length = 0
self.exits = set()
def __repr__(self):
- return "<%d+%d @%d %r>" % (
- self.byte, self.length, self.line, list(self.exits)
- )
-
-
-class AdHocMain(object): # pragma: no cover
- """An ad-hoc main for code parsing experiments."""
-
- def main(self, args):
- """A main function for trying the code from the command line."""
-
- from optparse import OptionParser
-
- parser = OptionParser()
- parser.add_option(
- "-c", action="store_true", dest="chunks",
- help="Show basic block chunks"
- )
- parser.add_option(
- "-d", action="store_true", dest="dis",
- help="Disassemble"
- )
- parser.add_option(
- "-R", action="store_true", dest="recursive",
- help="Recurse to find source files"
- )
- parser.add_option(
- "-s", action="store_true", dest="source",
- help="Show analyzed source"
- )
- parser.add_option(
- "-t", action="store_true", dest="tokens",
- help="Show tokens"
- )
-
- options, args = parser.parse_args()
- if options.recursive:
- if args:
- root = args[0]
- else:
- root = "."
- for root, _, _ in os.walk(root):
- for f in glob.glob(root + "/*.py"):
- self.adhoc_one_file(options, f)
+ if self.first:
+ bang = "!"
else:
- self.adhoc_one_file(options, args[0])
+ bang = ""
+ return "<%d+%d @%d%s %r>" % (
+ self.byte, self.length, self.line, bang, list(self.exits)
+ )
- def adhoc_one_file(self, options, filename):
- """Process just one file."""
- if options.dis or options.chunks:
- try:
- bp = ByteParser(filename=filename)
- except CoverageException:
- _, err, _ = sys.exc_info()
- print("%s" % (err,))
- return
+class CachedTokenizer(object):
+ """A one-element cache around tokenize.generate_tokens.
- if options.dis:
- print("Main code:")
- bp._disassemble()
+ When reporting, coverage.py tokenizes files twice, once to find the
+ structure of the file, and once to syntax-color it. Tokenizing is
+ expensive, and easily cached.
- if options.chunks:
- chunks = bp._all_chunks()
- if options.recursive:
- print("%6d: %s" % (len(chunks), filename))
- else:
- print("Chunks: %r" % chunks)
- arcs = bp._all_arcs()
- print("Arcs: %r" % sorted(arcs))
+ This is a one-element cache so that our twice-in-a-row tokenizing doesn't
+ actually tokenize twice.
- if options.source or options.tokens:
- cp = CodeParser(filename=filename, exclude=r"no\s*cover")
- cp.show_tokens = options.tokens
- cp._raw_parse()
+ """
+ def __init__(self):
+ self.last_text = None
+ self.last_tokens = None
- if options.source:
- if options.chunks:
- arc_width, arc_chars = self.arc_ascii_art(arcs)
- else:
- arc_width, arc_chars = 0, {}
+ def generate_tokens(self, text):
+ """A stand-in for `tokenize.generate_tokens`."""
+ if text != self.last_text:
+ self.last_text = text
+ self.last_tokens = list(
+ tokenize.generate_tokens(StringIO(text).readline)
+ )
+ return self.last_tokens
- exit_counts = cp.exit_counts()
-
- for i, ltext in enumerate(cp.lines):
- lineno = i+1
- m0 = m1 = m2 = m3 = a = ' '
- if lineno in cp.statement_starts:
- m0 = '-'
- exits = exit_counts.get(lineno, 0)
- if exits > 1:
- m1 = str(exits)
- if lineno in cp.docstrings:
- m2 = '"'
- if lineno in cp.classdefs:
- m2 = 'C'
- if lineno in cp.excluded:
- m3 = 'x'
- a = arc_chars.get(lineno, '').ljust(arc_width)
- print("%4d %s%s%s%s%s %s" %
- (lineno, m0, m1, m2, m3, a, ltext)
- )
-
- def arc_ascii_art(self, arcs):
- """Draw arcs as ascii art.
-
- Returns a width of characters needed to draw all the arcs, and a
- dictionary mapping line numbers to ascii strings to draw for that line.
-
- """
- arc_chars = {}
- for lfrom, lto in sorted(arcs):
- if lfrom < 0:
- arc_chars[lto] = arc_chars.get(lto, '') + 'v'
- elif lto < 0:
- arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^'
- else:
- if lfrom == lto - 1:
- # Don't show obvious arcs.
- continue
- if lfrom < lto:
- l1, l2 = lfrom, lto
- else:
- l1, l2 = lto, lfrom
- w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)])
- for l in range(l1, l2+1):
- if l == lfrom:
- ch = '<'
- elif l == lto:
- ch = '>'
- else:
- ch = '|'
- arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch
- arc_width = 0
-
- if arc_chars:
- arc_width = max([len(a) for a in arc_chars.values()])
- else:
- arc_width = 0
-
- return arc_width, arc_chars
-
-if __name__ == '__main__':
- AdHocMain().main(sys.argv[1:])
+# Create our generate_tokens cache as a callable replacement function.
+generate_tokens = CachedTokenizer().generate_tokens
diff --git a/python/helpers/coverage/phystokens.py b/python/helpers/coverage/phystokens.py
index fc4f2c9..99b1d5b 100644
--- a/python/helpers/coverage/phystokens.py
+++ b/python/helpers/coverage/phystokens.py
@@ -1,7 +1,9 @@
"""Better tokenizing for coverage.py."""
-import keyword, re, token, tokenize
-from coverage.backward import StringIO # pylint: disable=W0622
+import codecs, keyword, re, sys, token, tokenize
+from coverage.backward import set # pylint: disable=W0622
+from coverage.parser import generate_tokens
+
def phys_tokens(toks):
"""Return all physical tokens, even line continuations.
@@ -18,7 +20,7 @@
last_ttype = None
for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
if last_lineno != elineno:
- if last_line and last_line[-2:] == "\\\n":
+ if last_line and last_line.endswith("\\\n"):
# We are at the beginning of a new line, and the last line
# ended with a backslash. We probably have to inject a
# backslash token into the stream. Unfortunately, there's more
@@ -74,11 +76,11 @@
is indistinguishable from a final line with a newline.
"""
- ws_tokens = [token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL]
+ ws_tokens = set([token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL])
line = []
col = 0
source = source.expandtabs(8).replace('\r\n', '\n')
- tokgen = tokenize.generate_tokens(StringIO(source).readline)
+ tokgen = generate_tokens(source)
for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
mark_start = True
for part in re.split('(\n)', ttext):
@@ -106,3 +108,103 @@
if line:
yield line
+
+def source_encoding(source):
+ """Determine the encoding for `source` (a string), according to PEP 263.
+
+ Returns a string, the name of the encoding.
+
+ """
+ # Note: this function should never be called on Python 3, since py3 has
+ # built-in tools to do this.
+ assert sys.version_info < (3, 0)
+
+ # This is mostly code adapted from Py3.2's tokenize module.
+
+ cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)")
+
+ # Do this so the detect_encode code we copied will work.
+ readline = iter(source.splitlines(True)).next
+
+ def _get_normal_name(orig_enc):
+ """Imitates get_normal_name in tokenizer.c."""
+ # Only care about the first 12 characters.
+ enc = orig_enc[:12].lower().replace("_", "-")
+ if re.match(r"^utf-8($|-)", enc):
+ return "utf-8"
+ if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc):
+ return "iso-8859-1"
+ return orig_enc
+
+ # From detect_encode():
+ # It detects the encoding from the presence of a utf-8 bom or an encoding
+ # cookie as specified in pep-0263. If both a bom and a cookie are present,
+ # but disagree, a SyntaxError will be raised. If the encoding cookie is an
+ # invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found,
+ # 'utf-8-sig' is returned.
+
+ # If no encoding is specified, then the default will be returned. The
+ # default varied with version.
+
+ if sys.version_info <= (2, 4):
+ default = 'iso-8859-1'
+ else:
+ default = 'ascii'
+
+ bom_found = False
+ encoding = None
+
+ def read_or_stop():
+ """Get the next source line, or ''."""
+ try:
+ return readline()
+ except StopIteration:
+ return ''
+
+ def find_cookie(line):
+ """Find an encoding cookie in `line`."""
+ try:
+ line_string = line.decode('ascii')
+ except UnicodeDecodeError:
+ return None
+
+ matches = cookie_re.findall(line_string)
+ if not matches:
+ return None
+ encoding = _get_normal_name(matches[0])
+ try:
+ codec = codecs.lookup(encoding)
+ except LookupError:
+ # This behaviour mimics the Python interpreter
+ raise SyntaxError("unknown encoding: " + encoding)
+
+ if bom_found:
+ # codecs in 2.3 were raw tuples of functions, assume the best.
+ codec_name = getattr(codec, 'name', encoding)
+ if codec_name != 'utf-8':
+ # This behaviour mimics the Python interpreter
+ raise SyntaxError('encoding problem: utf-8')
+ encoding += '-sig'
+ return encoding
+
+ first = read_or_stop()
+ if first.startswith(codecs.BOM_UTF8):
+ bom_found = True
+ first = first[3:]
+ default = 'utf-8-sig'
+ if not first:
+ return default
+
+ encoding = find_cookie(first)
+ if encoding:
+ return encoding
+
+ second = read_or_stop()
+ if not second:
+ return default
+
+ encoding = find_cookie(second)
+ if encoding:
+ return encoding
+
+ return default
diff --git a/python/helpers/coverage/report.py b/python/helpers/coverage/report.py
index 6c5510a..34f4442 100644
--- a/python/helpers/coverage/report.py
+++ b/python/helpers/coverage/report.py
@@ -2,20 +2,21 @@
import fnmatch, os
from coverage.codeunit import code_unit_factory
+from coverage.files import prep_patterns
from coverage.misc import CoverageException, NoSource, NotPython
class Reporter(object):
"""A base class for all reporters."""
- def __init__(self, coverage, ignore_errors=False):
+ def __init__(self, coverage, config):
"""Create a reporter.
- `coverage` is the coverage instance. `ignore_errors` controls how
- skittish the reporter will be during file processing.
+ `coverage` is the coverage instance. `config` is an instance of
+ CoverageConfig, for controlling all sorts of behavior.
"""
self.coverage = coverage
- self.ignore_errors = ignore_errors
+ self.config = config
# The code units to report on. Set by find_code_units.
self.code_units = []
@@ -24,19 +25,18 @@
# classes.
self.directory = None
- def find_code_units(self, morfs, config):
+ def find_code_units(self, morfs):
"""Find the code units we'll report on.
- `morfs` is a list of modules or filenames. `config` is a
- CoverageConfig instance.
+ `morfs` is a list of modules or filenames.
"""
morfs = morfs or self.coverage.data.measured_files()
file_locator = self.coverage.file_locator
self.code_units = code_unit_factory(morfs, file_locator)
- if config.include:
- patterns = [file_locator.abs_file(p) for p in config.include]
+ if self.config.include:
+ patterns = prep_patterns(self.config.include)
filtered = []
for cu in self.code_units:
for pattern in patterns:
@@ -45,8 +45,8 @@
break
self.code_units = filtered
- if config.omit:
- patterns = [file_locator.abs_file(p) for p in config.omit]
+ if self.config.omit:
+ patterns = prep_patterns(self.config.omit)
filtered = []
for cu in self.code_units:
for pattern in patterns:
@@ -58,7 +58,7 @@
self.code_units.sort()
- def report_files(self, report_fn, morfs, config, directory=None):
+ def report_files(self, report_fn, morfs, directory=None):
"""Run a reporting function on a number of morfs.
`report_fn` is called for each relative morf in `morfs`. It is called
@@ -69,10 +69,8 @@
where `code_unit` is the `CodeUnit` for the morf, and `analysis` is
the `Analysis` for the morf.
- `config` is a CoverageConfig instance.
-
"""
- self.find_code_units(morfs, config)
+ self.find_code_units(morfs)
if not self.code_units:
raise CoverageException("No data to report.")
@@ -84,6 +82,11 @@
for cu in self.code_units:
try:
report_fn(cu, self.coverage._analyze(cu))
- except (NoSource, NotPython):
- if not self.ignore_errors:
+ except NoSource:
+ if not self.config.ignore_errors:
+ raise
+ except NotPython:
+ # Only report errors for .py files, and only if we didn't
+ # explicitly suppress those errors.
+ if cu.should_be_python() and not self.config.ignore_errors:
raise
diff --git a/python/helpers/coverage/results.py b/python/helpers/coverage/results.py
index adfb8f4..db6df0d 100644
--- a/python/helpers/coverage/results.py
+++ b/python/helpers/coverage/results.py
@@ -2,7 +2,7 @@
import os
-from coverage.backward import set, sorted # pylint: disable=W0622
+from coverage.backward import iitems, set, sorted # pylint: disable=W0622
from coverage.misc import format_lines, join_regex, NoSource
from coverage.parser import CodeParser
@@ -15,16 +15,10 @@
self.code_unit = code_unit
self.filename = self.code_unit.filename
- ext = os.path.splitext(self.filename)[1]
- source = None
- if ext == '.py':
- if not os.path.exists(self.filename):
- source = self.coverage.file_locator.get_zip_data(self.filename)
- if not source:
- raise NoSource("No source for code: %r" % self.filename)
+ actual_filename, source = self.find_source(self.filename)
self.parser = CodeParser(
- text=source, filename=self.filename,
+ text=source, filename=actual_filename,
exclude=self.coverage._exclude_regex('exclude')
)
self.statements, self.excluded = self.parser.parse_source()
@@ -32,7 +26,7 @@
# Identify missing statements.
executed = self.coverage.data.executed_lines(self.filename)
exec1 = self.parser.first_lines(executed)
- self.missing = sorted(set(self.statements) - set(exec1))
+ self.missing = self.statements - exec1
if self.coverage.data.has_arcs():
self.no_branch = self.parser.lines_matching(
@@ -41,9 +35,12 @@
)
n_branches = self.total_branches()
mba = self.missing_branch_arcs()
- n_missing_branches = sum([len(v) for v in mba.values()])
+ n_partial_branches = sum(
+ [len(v) for k,v in iitems(mba) if k not in self.missing]
+ )
+ n_missing_branches = sum([len(v) for k,v in iitems(mba)])
else:
- n_branches = n_missing_branches = 0
+ n_branches = n_partial_branches = n_missing_branches = 0
self.no_branch = set()
self.numbers = Numbers(
@@ -52,9 +49,48 @@
n_excluded=len(self.excluded),
n_missing=len(self.missing),
n_branches=n_branches,
+ n_partial_branches=n_partial_branches,
n_missing_branches=n_missing_branches,
)
+ def find_source(self, filename):
+ """Find the source for `filename`.
+
+ Returns two values: the actual filename, and the source.
+
+ The source returned depends on which of these cases holds:
+
+ * The filename seems to be a non-source file: returns None
+
+ * The filename is a source file, and actually exists: returns None.
+
+ * The filename is a source file, and is in a zip file or egg:
+ returns the source.
+
+ * The filename is a source file, but couldn't be found: raises
+ `NoSource`.
+
+ """
+ source = None
+
+ base, ext = os.path.splitext(filename)
+ TRY_EXTS = {
+ '.py': ['.py', '.pyw'],
+ '.pyw': ['.pyw'],
+ }
+ try_exts = TRY_EXTS.get(ext)
+ if not try_exts:
+ return filename, None
+
+ for try_ext in try_exts:
+ try_filename = base + try_ext
+ if os.path.exists(try_filename):
+ return try_filename, None
+ source = self.coverage.file_locator.get_zip_data(try_filename)
+ if source:
+ return try_filename, source
+ raise NoSource("No source for code: '%s'" % filename)
+
def missing_formatted(self):
"""The missing line numbers, formatted nicely.
@@ -107,7 +143,7 @@
def branch_lines(self):
"""Returns a list of line numbers that have more than one exit."""
exit_counts = self.parser.exit_counts()
- return [l1 for l1,count in exit_counts.items() if count > 1]
+ return [l1 for l1,count in iitems(exit_counts) if count > 1]
def total_branches(self):
"""How many total branches are there?"""
@@ -164,13 +200,14 @@
_near100 = 99.0
def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
- n_branches=0, n_missing_branches=0
+ n_branches=0, n_partial_branches=0, n_missing_branches=0
):
self.n_files = n_files
self.n_statements = n_statements
self.n_excluded = n_excluded
self.n_missing = n_missing
self.n_branches = n_branches
+ self.n_partial_branches = n_partial_branches
self.n_missing_branches = n_missing_branches
def set_precision(cls, precision):
@@ -234,8 +271,12 @@
nums.n_excluded = self.n_excluded + other.n_excluded
nums.n_missing = self.n_missing + other.n_missing
nums.n_branches = self.n_branches + other.n_branches
- nums.n_missing_branches = (self.n_missing_branches +
- other.n_missing_branches)
+ nums.n_partial_branches = (
+ self.n_partial_branches + other.n_partial_branches
+ )
+ nums.n_missing_branches = (
+ self.n_missing_branches + other.n_missing_branches
+ )
return nums
def __radd__(self, other):
diff --git a/python/helpers/coverage/summary.py b/python/helpers/coverage/summary.py
index 599ae78..c99c530 100644
--- a/python/helpers/coverage/summary.py
+++ b/python/helpers/coverage/summary.py
@@ -4,24 +4,23 @@
from coverage.report import Reporter
from coverage.results import Numbers
+from coverage.misc import NotPython
class SummaryReporter(Reporter):
"""A reporter for writing the summary report."""
- def __init__(self, coverage, show_missing=True, ignore_errors=False):
- super(SummaryReporter, self).__init__(coverage, ignore_errors)
- self.show_missing = show_missing
+ def __init__(self, coverage, config):
+ super(SummaryReporter, self).__init__(coverage, config)
self.branches = coverage.data.has_arcs()
- def report(self, morfs, outfile=None, config=None):
+ def report(self, morfs, outfile=None):
"""Writes a report summarizing coverage statistics per module.
- `outfile` is a file object to write the summary to. `config` is a
- CoverageConfig instance.
+ `outfile` is a file object to write the summary to.
"""
- self.find_code_units(morfs, config)
+ self.find_code_units(morfs)
# Prepare the formatting strings
max_name = max([len(cu.name) for cu in self.code_units] + [5])
@@ -30,12 +29,12 @@
header = (fmt_name % "Name") + " Stmts Miss"
fmt_coverage = fmt_name + "%6d %6d"
if self.branches:
- header += " Branch BrPart"
+ header += " Branch BrMiss"
fmt_coverage += " %6d %6d"
width100 = Numbers.pc_str_width()
header += "%*s" % (width100+4, "Cover")
fmt_coverage += "%%%ds%%%%" % (width100+3,)
- if self.show_missing:
+ if self.config.show_missing:
header += " Missing"
fmt_coverage += " %s"
rule = "-" * len(header) + "\n"
@@ -59,15 +58,19 @@
if self.branches:
args += (nums.n_branches, nums.n_missing_branches)
args += (nums.pc_covered_str,)
- if self.show_missing:
+ if self.config.show_missing:
args += (analysis.missing_formatted(),)
outfile.write(fmt_coverage % args)
total += nums
- except KeyboardInterrupt: # pragma: no cover
+ except KeyboardInterrupt: # pragma: not covered
raise
except:
- if not self.ignore_errors:
+ report_it = not self.config.ignore_errors
+ if report_it:
typ, msg = sys.exc_info()[:2]
+ if typ is NotPython and not cu.should_be_python():
+ report_it = False
+ if report_it:
outfile.write(fmt_err % (cu.name, typ.__name__, msg))
if total.n_files > 1:
@@ -76,6 +79,8 @@
if self.branches:
args += (total.n_branches, total.n_missing_branches)
args += (total.pc_covered_str,)
- if self.show_missing:
+ if self.config.show_missing:
args += ("",)
outfile.write(fmt_coverage % args)
+
+ return total.pc_covered
diff --git a/python/helpers/coverage/templite.py b/python/helpers/coverage/templite.py
index c39e061..e5c0baf 100644
--- a/python/helpers/coverage/templite.py
+++ b/python/helpers/coverage/templite.py
@@ -2,7 +2,53 @@
# Coincidentally named the same as http://code.activestate.com/recipes/496702/
-import re, sys
+import re
+
+from coverage.backward import set # pylint: disable=W0622
+
+
+class CodeBuilder(object):
+ """Build source code conveniently."""
+
+ def __init__(self, indent=0):
+ self.code = []
+ self.indent_amount = indent
+
+ def add_line(self, line):
+ """Add a line of source to the code.
+
+ Don't include indentations or newlines.
+
+ """
+ self.code.append(" " * self.indent_amount)
+ self.code.append(line)
+ self.code.append("\n")
+
+ def add_section(self):
+ """Add a section, a sub-CodeBuilder."""
+ sect = CodeBuilder(self.indent_amount)
+ self.code.append(sect)
+ return sect
+
+ def indent(self):
+ """Increase the current indent for following lines."""
+ self.indent_amount += 4
+
+ def dedent(self):
+ """Decrease the current indent for following lines."""
+ self.indent_amount -= 4
+
+ def __str__(self):
+ return "".join([str(c) for c in self.code])
+
+ def get_function(self, fn_name):
+ """Compile the code, and return the function `fn_name`."""
+ assert self.indent_amount == 0
+ g = {}
+ code_text = str(self)
+ exec(code_text, g)
+ return g[fn_name]
+
class Templite(object):
"""A simple template renderer, for a nano-subset of Django syntax.
@@ -39,53 +85,104 @@
for context in contexts:
self.context.update(context)
+ # We construct a function in source form, then compile it and hold onto
+ # it, and execute it to render the template.
+ code = CodeBuilder()
+
+ code.add_line("def render(ctx, dot):")
+ code.indent()
+ vars_code = code.add_section()
+ self.all_vars = set()
+ self.loop_vars = set()
+ code.add_line("result = []")
+ code.add_line("a = result.append")
+ code.add_line("e = result.extend")
+ code.add_line("s = str")
+
+ buffered = []
+ def flush_output():
+ """Force `buffered` to the code builder."""
+ if len(buffered) == 1:
+ code.add_line("a(%s)" % buffered[0])
+ elif len(buffered) > 1:
+ code.add_line("e([%s])" % ",".join(buffered))
+ del buffered[:]
+
# Split the text to form a list of tokens.
toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
- # Parse the tokens into a nested list of operations. Each item in the
- # list is a tuple with an opcode, and arguments. They'll be
- # interpreted by TempliteEngine.
- #
- # When parsing an action tag with nested content (if, for), the current
- # ops list is pushed onto ops_stack, and the parsing continues in a new
- # ops list that is part of the arguments to the if or for op.
- ops = []
ops_stack = []
for tok in toks:
if tok.startswith('{{'):
- # Expression: ('exp', expr)
- ops.append(('exp', tok[2:-2].strip()))
+ # An expression to evaluate.
+ buffered.append("s(%s)" % self.expr_code(tok[2:-2].strip()))
elif tok.startswith('{#'):
# Comment: ignore it and move on.
continue
elif tok.startswith('{%'):
# Action tag: split into words and parse further.
+ flush_output()
words = tok[2:-2].strip().split()
if words[0] == 'if':
- # If: ('if', (expr, body_ops))
- if_ops = []
+ # An if statement: evaluate the expression to determine if.
assert len(words) == 2
- ops.append(('if', (words[1], if_ops)))
- ops_stack.append(ops)
- ops = if_ops
+ ops_stack.append('if')
+ code.add_line("if %s:" % self.expr_code(words[1]))
+ code.indent()
elif words[0] == 'for':
- # For: ('for', (varname, listexpr, body_ops))
+ # A loop: iterate over expression result.
assert len(words) == 4 and words[2] == 'in'
- for_ops = []
- ops.append(('for', (words[1], words[3], for_ops)))
- ops_stack.append(ops)
- ops = for_ops
+ ops_stack.append('for')
+ self.loop_vars.add(words[1])
+ code.add_line(
+ "for c_%s in %s:" % (
+ words[1],
+ self.expr_code(words[3])
+ )
+ )
+ code.indent()
elif words[0].startswith('end'):
# Endsomething. Pop the ops stack
- ops = ops_stack.pop()
- assert ops[-1][0] == words[0][3:]
+ end_what = words[0][3:]
+ if ops_stack[-1] != end_what:
+ raise SyntaxError("Mismatched end tag: %r" % end_what)
+ ops_stack.pop()
+ code.dedent()
else:
- raise SyntaxError("Don't understand tag %r" % words)
+ raise SyntaxError("Don't understand tag: %r" % words[0])
else:
- ops.append(('lit', tok))
+ # Literal content. If it isn't empty, output it.
+ if tok:
+ buffered.append("%r" % tok)
+ flush_output()
- assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0]
- self.ops = ops
+ for var_name in self.all_vars - self.loop_vars:
+ vars_code.add_line("c_%s = ctx[%r]" % (var_name, var_name))
+
+ if ops_stack:
+ raise SyntaxError("Unmatched action tag: %r" % ops_stack[-1])
+
+ code.add_line("return ''.join(result)")
+ code.dedent()
+ self.render_function = code.get_function('render')
+
+ def expr_code(self, expr):
+ """Generate a Python expression for `expr`."""
+ if "|" in expr:
+ pipes = expr.split("|")
+ code = self.expr_code(pipes[0])
+ for func in pipes[1:]:
+ self.all_vars.add(func)
+ code = "c_%s(%s)" % (func, code)
+ elif "." in expr:
+ dots = expr.split(".")
+ code = self.expr_code(dots[0])
+ args = [repr(d) for d in dots[1:]]
+ code = "dot(%s, %s)" % (code, ", ".join(args))
+ else:
+ self.all_vars.add(expr)
+ code = "c_%s" % expr
+ return code
def render(self, context=None):
"""Render this template by applying it to `context`.
@@ -97,70 +194,15 @@
ctx = dict(self.context)
if context:
ctx.update(context)
+ return self.render_function(ctx, self.do_dots)
- # Run it through an engine, and return the result.
- engine = _TempliteEngine(ctx)
- engine.execute(self.ops)
- return "".join(engine.result)
-
-
-class _TempliteEngine(object):
- """Executes Templite objects to produce strings."""
- def __init__(self, context):
- self.context = context
- self.result = []
-
- def execute(self, ops):
- """Execute `ops` in the engine.
-
- Called recursively for the bodies of if's and loops.
-
- """
- for op, args in ops:
- if op == 'lit':
- self.result.append(args)
- elif op == 'exp':
- try:
- self.result.append(str(self.evaluate(args)))
- except:
- exc_class, exc, _ = sys.exc_info()
- new_exc = exc_class("Couldn't evaluate {{ %s }}: %s"
- % (args, exc))
- raise new_exc
- elif op == 'if':
- expr, body = args
- if self.evaluate(expr):
- self.execute(body)
- elif op == 'for':
- var, lis, body = args
- vals = self.evaluate(lis)
- for val in vals:
- self.context[var] = val
- self.execute(body)
- else:
- raise AssertionError("TempliteEngine doesn't grok op %r" % op)
-
- def evaluate(self, expr):
- """Evaluate an expression.
-
- `expr` can have pipes and dots to indicate data access and filtering.
-
- """
- if "|" in expr:
- pipes = expr.split("|")
- value = self.evaluate(pipes[0])
- for func in pipes[1:]:
- value = self.evaluate(func)(value)
- elif "." in expr:
- dots = expr.split('.')
- value = self.evaluate(dots[0])
- for dot in dots[1:]:
- try:
- value = getattr(value, dot)
- except AttributeError:
- value = value[dot]
- if hasattr(value, '__call__'):
- value = value()
- else:
- value = self.context[expr]
+ def do_dots(self, value, *dots):
+ """Evaluate dotted expressions at runtime."""
+ for dot in dots:
+ try:
+ value = getattr(value, dot)
+ except AttributeError:
+ value = value[dot]
+ if hasattr(value, '__call__'):
+ value = value()
return value
diff --git a/python/helpers/coverage/tracer.c b/python/helpers/coverage/tracer.c
new file mode 100644
index 0000000..97dd113
--- /dev/null
+++ b/python/helpers/coverage/tracer.c
@@ -0,0 +1,730 @@
+/* C-based Tracer for Coverage. */
+
+#include "Python.h"
+#include "compile.h" /* in 2.3, this wasn't part of Python.h */
+#include "eval.h" /* or this. */
+#include "structmember.h"
+#include "frameobject.h"
+
+/* Compile-time debugging helpers */
+#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
+#undef TRACE_LOG /* Define to log our bookkeeping. */
+#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
+
+#if COLLECT_STATS
+#define STATS(x) x
+#else
+#define STATS(x)
+#endif
+
+/* Py 2.x and 3.x compatibility */
+
+#ifndef Py_TYPE
+#define Py_TYPE(o) (((PyObject*)(o))->ob_type)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+#define MyText_Type PyUnicode_Type
+#define MyText_Check(o) PyUnicode_Check(o)
+#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
+#define MyText_AS_STRING(o) PyBytes_AS_STRING(o)
+#define MyInt_FromLong(l) PyLong_FromLong(l)
+
+#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
+
+#else
+
+#define MyText_Type PyString_Type
+#define MyText_Check(o) PyString_Check(o)
+#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
+#define MyText_AS_STRING(o) PyString_AS_STRING(o)
+#define MyInt_FromLong(l) PyInt_FromLong(l)
+
+#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
+
+#endif /* Py3k */
+
+/* The values returned to indicate ok or error. */
+#define RET_OK 0
+#define RET_ERROR -1
+
+/* An entry on the data stack. For each call frame, we need to record the
+ dictionary to capture data, and the last line number executed in that
+ frame.
+*/
+typedef struct {
+ PyObject * file_data; /* PyMem_Malloc'ed, a borrowed ref. */
+ int last_line;
+} DataStackEntry;
+
+/* The CTracer type. */
+
+typedef struct {
+ PyObject_HEAD
+
+ /* Python objects manipulated directly by the Collector class. */
+ PyObject * should_trace;
+ PyObject * warn;
+ PyObject * data;
+ PyObject * should_trace_cache;
+ PyObject * arcs;
+
+ /* Has the tracer been started? */
+ int started;
+ /* Are we tracing arcs, or just lines? */
+ int tracing_arcs;
+
+ /*
+ The data stack is a stack of dictionaries. Each dictionary collects
+ data for a single source file. The data stack parallels the call stack:
+ each call pushes the new frame's file data onto the data stack, and each
+ return pops file data off.
+
+ The file data is a dictionary whose form depends on the tracing options.
+ If tracing arcs, the keys are line number pairs. If not tracing arcs,
+ the keys are line numbers. In both cases, the value is irrelevant
+ (None).
+ */
+ /* The index of the last-used entry in data_stack. */
+ int depth;
+ /* The file data at each level, or NULL if not recording. */
+ DataStackEntry * data_stack;
+ int data_stack_alloc; /* number of entries allocated at data_stack. */
+
+ /* The current file_data dictionary. Borrowed. */
+ PyObject * cur_file_data;
+
+ /* The line number of the last line recorded, for tracing arcs.
+ -1 means there was no previous line, as when entering a code object.
+ */
+ int last_line;
+
+ /* The parent frame for the last exception event, to fix missing returns. */
+ PyFrameObject * last_exc_back;
+ int last_exc_firstlineno;
+
+#if COLLECT_STATS
+ struct {
+ unsigned int calls;
+ unsigned int lines;
+ unsigned int returns;
+ unsigned int exceptions;
+ unsigned int others;
+ unsigned int new_files;
+ unsigned int missed_returns;
+ unsigned int stack_reallocs;
+ unsigned int errors;
+ } stats;
+#endif /* COLLECT_STATS */
+} CTracer;
+
+#define STACK_DELTA 100
+
+static int
+CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
+{
+#if COLLECT_STATS
+ self->stats.calls = 0;
+ self->stats.lines = 0;
+ self->stats.returns = 0;
+ self->stats.exceptions = 0;
+ self->stats.others = 0;
+ self->stats.new_files = 0;
+ self->stats.missed_returns = 0;
+ self->stats.stack_reallocs = 0;
+ self->stats.errors = 0;
+#endif /* COLLECT_STATS */
+
+ self->should_trace = NULL;
+ self->warn = NULL;
+ self->data = NULL;
+ self->should_trace_cache = NULL;
+ self->arcs = NULL;
+
+ self->started = 0;
+ self->tracing_arcs = 0;
+
+ self->depth = -1;
+ self->data_stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry));
+ if (self->data_stack == NULL) {
+ STATS( self->stats.errors++; )
+ PyErr_NoMemory();
+ return RET_ERROR;
+ }
+ self->data_stack_alloc = STACK_DELTA;
+
+ self->cur_file_data = NULL;
+ self->last_line = -1;
+
+ self->last_exc_back = NULL;
+
+ return RET_OK;
+}
+
+static void
+CTracer_dealloc(CTracer *self)
+{
+ if (self->started) {
+ PyEval_SetTrace(NULL, NULL);
+ }
+
+ Py_XDECREF(self->should_trace);
+ Py_XDECREF(self->warn);
+ Py_XDECREF(self->data);
+ Py_XDECREF(self->should_trace_cache);
+
+ PyMem_Free(self->data_stack);
+
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+#if TRACE_LOG
+static const char *
+indent(int n)
+{
+ static const char * spaces =
+ " "
+ " "
+ " "
+ " "
+ ;
+ return spaces + strlen(spaces) - n*2;
+}
+
+static int logging = 0;
+/* Set these constants to be a file substring and line number to start logging. */
+static const char * start_file = "tests/views";
+static int start_line = 27;
+
+static void
+showlog(int depth, int lineno, PyObject * filename, const char * msg)
+{
+ if (logging) {
+ printf("%s%3d ", indent(depth), depth);
+ if (lineno) {
+ printf("%4d", lineno);
+ }
+ else {
+ printf(" ");
+ }
+ if (filename) {
+ PyObject *ascii = MyText_AS_BYTES(filename);
+ printf(" %s", MyText_AS_STRING(ascii));
+ Py_DECREF(ascii);
+ }
+ if (msg) {
+ printf(" %s", msg);
+ }
+ printf("\n");
+ }
+}
+
+#define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
+#else
+#define SHOWLOG(a,b,c,d)
+#endif /* TRACE_LOG */
+
+#if WHAT_LOG
+static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
+#endif
+
+/* Record a pair of integers in self->cur_file_data. */
+static int
+CTracer_record_pair(CTracer *self, int l1, int l2)
+{
+ int ret = RET_OK;
+
+ PyObject * t = Py_BuildValue("(ii)", l1, l2);
+ if (t != NULL) {
+ if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) {
+ STATS( self->stats.errors++; )
+ ret = RET_ERROR;
+ }
+ Py_DECREF(t);
+ }
+ else {
+ STATS( self->stats.errors++; )
+ ret = RET_ERROR;
+ }
+ return ret;
+}
+
+/*
+ * The Trace Function
+ */
+static int
+CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
+{
+ int ret = RET_OK;
+ PyObject * filename = NULL;
+ PyObject * tracename = NULL;
+ #if WHAT_LOG || TRACE_LOG
+ PyObject * ascii = NULL;
+ #endif
+
+ #if WHAT_LOG
+ if (what <= sizeof(what_sym)/sizeof(const char *)) {
+ ascii = MyText_AS_BYTES(frame->f_code->co_filename);
+ printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(ascii), frame->f_lineno);
+ Py_DECREF(ascii);
+ }
+ #endif
+
+ #if TRACE_LOG
+ ascii = MyText_AS_BYTES(frame->f_code->co_filename);
+ if (strstr(MyText_AS_STRING(ascii), start_file) && frame->f_lineno == start_line) {
+ logging = 1;
+ }
+ Py_DECREF(ascii);
+ #endif
+
+ /* See below for details on missing-return detection. */
+ if (self->last_exc_back) {
+ if (frame == self->last_exc_back) {
+ /* Looks like someone forgot to send a return event. We'll clear
+ the exception state and do the RETURN code here. Notice that the
+ frame we have in hand here is not the correct frame for the RETURN,
+ that frame is gone. Our handling for RETURN doesn't need the
+ actual frame, but we do log it, so that will look a little off if
+ you're looking at the detailed log.
+
+ If someday we need to examine the frame when doing RETURN, then
+ we'll need to keep more of the missed frame's state.
+ */
+ STATS( self->stats.missed_returns++; )
+ if (self->depth >= 0) {
+ if (self->tracing_arcs && self->cur_file_data) {
+ if (CTracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) {
+ return RET_ERROR;
+ }
+ }
+ SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn");
+ self->cur_file_data = self->data_stack[self->depth].file_data;
+ self->last_line = self->data_stack[self->depth].last_line;
+ self->depth--;
+ }
+ }
+ self->last_exc_back = NULL;
+ }
+
+
+ switch (what) {
+ case PyTrace_CALL: /* 0 */
+ STATS( self->stats.calls++; )
+ /* Grow the stack. */
+ self->depth++;
+ if (self->depth >= self->data_stack_alloc) {
+ STATS( self->stats.stack_reallocs++; )
+ /* We've outgrown our data_stack array: make it bigger. */
+ int bigger = self->data_stack_alloc + STACK_DELTA;
+ DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack, bigger * sizeof(DataStackEntry));
+ if (bigger_data_stack == NULL) {
+ STATS( self->stats.errors++; )
+ PyErr_NoMemory();
+ self->depth--;
+ return RET_ERROR;
+ }
+ self->data_stack = bigger_data_stack;
+ self->data_stack_alloc = bigger;
+ }
+
+ /* Push the current state on the stack. */
+ self->data_stack[self->depth].file_data = self->cur_file_data;
+ self->data_stack[self->depth].last_line = self->last_line;
+
+ /* Check if we should trace this line. */
+ filename = frame->f_code->co_filename;
+ tracename = PyDict_GetItem(self->should_trace_cache, filename);
+ if (tracename == NULL) {
+ STATS( self->stats.new_files++; )
+ /* We've never considered this file before. */
+ /* Ask should_trace about it. */
+ PyObject * args = Py_BuildValue("(OO)", filename, frame);
+ tracename = PyObject_Call(self->should_trace, args, NULL);
+ Py_DECREF(args);
+ if (tracename == NULL) {
+ /* An error occurred inside should_trace. */
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ if (PyDict_SetItem(self->should_trace_cache, filename, tracename) < 0) {
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ }
+ else {
+ Py_INCREF(tracename);
+ }
+
+ /* If tracename is a string, then we're supposed to trace. */
+ if (MyText_Check(tracename)) {
+ PyObject * file_data = PyDict_GetItem(self->data, tracename);
+ if (file_data == NULL) {
+ file_data = PyDict_New();
+ if (file_data == NULL) {
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ ret = PyDict_SetItem(self->data, tracename, file_data);
+ Py_DECREF(file_data);
+ if (ret < 0) {
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ }
+ self->cur_file_data = file_data;
+ /* Make the frame right in case settrace(gettrace()) happens. */
+ Py_INCREF(self);
+ frame->f_trace = (PyObject*)self;
+ SHOWLOG(self->depth, frame->f_lineno, filename, "traced");
+ }
+ else {
+ self->cur_file_data = NULL;
+ SHOWLOG(self->depth, frame->f_lineno, filename, "skipped");
+ }
+
+ Py_DECREF(tracename);
+
+ self->last_line = -1;
+ break;
+
+ case PyTrace_RETURN: /* 3 */
+ STATS( self->stats.returns++; )
+ /* A near-copy of this code is above in the missing-return handler. */
+ if (self->depth >= 0) {
+ if (self->tracing_arcs && self->cur_file_data) {
+ int first = frame->f_code->co_firstlineno;
+ if (CTracer_record_pair(self, self->last_line, -first) < 0) {
+ return RET_ERROR;
+ }
+ }
+
+ SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "return");
+ self->cur_file_data = self->data_stack[self->depth].file_data;
+ self->last_line = self->data_stack[self->depth].last_line;
+ self->depth--;
+ }
+ break;
+
+ case PyTrace_LINE: /* 2 */
+ STATS( self->stats.lines++; )
+ if (self->depth >= 0) {
+ SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "line");
+ if (self->cur_file_data) {
+ /* We're tracing in this frame: record something. */
+ if (self->tracing_arcs) {
+ /* Tracing arcs: key is (last_line,this_line). */
+ if (CTracer_record_pair(self, self->last_line, frame->f_lineno) < 0) {
+ return RET_ERROR;
+ }
+ }
+ else {
+ /* Tracing lines: key is simply this_line. */
+ PyObject * this_line = MyInt_FromLong(frame->f_lineno);
+ if (this_line == NULL) {
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ ret = PyDict_SetItem(self->cur_file_data, this_line, Py_None);
+ Py_DECREF(this_line);
+ if (ret < 0) {
+ STATS( self->stats.errors++; )
+ return RET_ERROR;
+ }
+ }
+ }
+ self->last_line = frame->f_lineno;
+ }
+ break;
+
+ case PyTrace_EXCEPTION:
+ /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
+ without a return event. To detect that, we'll keep a copy of the
+ parent frame for an exception event. If the next event is in that
+ frame, then we must have returned without a return event. We can
+ synthesize the missing event then.
+
+ Python itself fixed this problem in 2.4. Pyexpat still has the bug.
+ I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
+ If it gets fixed, this code should still work properly. Maybe some day
+ the bug will be fixed everywhere coverage.py is supported, and we can
+ remove this missing-return detection.
+
+ More about this fix: http://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
+ */
+ STATS( self->stats.exceptions++; )
+ self->last_exc_back = frame->f_back;
+ self->last_exc_firstlineno = frame->f_code->co_firstlineno;
+ break;
+
+ default:
+ STATS( self->stats.others++; )
+ break;
+ }
+
+ return RET_OK;
+}
+
+/*
+ * Python has two ways to set the trace function: sys.settrace(fn), which
+ * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
+ * a C function and a Python object. The way these work together is that
+ * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
+ * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
+ * simply returns the Python object used as the second argument to
+ * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
+ * means it must be callable to be used in sys.settrace().
+ *
+ * So we make our self callable, equivalent to invoking our trace function.
+ *
+ * To help with the process of replaying stored frames, this function has an
+ * optional keyword argument:
+ *
+ * def CTracer_call(frame, event, arg, lineno=0)
+ *
+ * If provided, the lineno argument is used as the line number, and the
+ * frame's f_lineno member is ignored.
+ */
+static PyObject *
+CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
+{
+ PyFrameObject *frame;
+ PyObject *what_str;
+ PyObject *arg;
+ int lineno = 0;
+ int what;
+ int orig_lineno;
+ PyObject *ret = NULL;
+
+ static char *what_names[] = {
+ "call", "exception", "line", "return",
+ "c_call", "c_exception", "c_return",
+ NULL
+ };
+
+ #if WHAT_LOG
+ printf("pytrace\n");
+ #endif
+
+ static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
+ &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
+ goto done;
+ }
+
+ /* In Python, the what argument is a string, we need to find an int
+ for the C function. */
+ for (what = 0; what_names[what]; what++) {
+ PyObject *ascii = MyText_AS_BYTES(what_str);
+ int should_break = !strcmp(MyText_AS_STRING(ascii), what_names[what]);
+ Py_DECREF(ascii);
+ if (should_break) {
+ break;
+ }
+ }
+
+ /* Save off the frame's lineno, and use the forced one, if provided. */
+ orig_lineno = frame->f_lineno;
+ if (lineno > 0) {
+ frame->f_lineno = lineno;
+ }
+
+ /* Invoke the C function, and return ourselves. */
+ if (CTracer_trace(self, frame, what, arg) == RET_OK) {
+ Py_INCREF(self);
+ ret = (PyObject *)self;
+ }
+
+ /* Clean up. */
+ frame->f_lineno = orig_lineno;
+
+done:
+ return ret;
+}
+
+static PyObject *
+CTracer_start(CTracer *self, PyObject *args_unused)
+{
+ PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
+ self->started = 1;
+ self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs);
+ self->last_line = -1;
+
+ /* start() returns a trace function usable with sys.settrace() */
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+static PyObject *
+CTracer_stop(CTracer *self, PyObject *args_unused)
+{
+ if (self->started) {
+ PyEval_SetTrace(NULL, NULL);
+ self->started = 0;
+ }
+
+ return Py_BuildValue("");
+}
+
+static PyObject *
+CTracer_get_stats(CTracer *self)
+{
+#if COLLECT_STATS
+ return Py_BuildValue(
+ "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI}",
+ "calls", self->stats.calls,
+ "lines", self->stats.lines,
+ "returns", self->stats.returns,
+ "exceptions", self->stats.exceptions,
+ "others", self->stats.others,
+ "new_files", self->stats.new_files,
+ "missed_returns", self->stats.missed_returns,
+ "stack_reallocs", self->stats.stack_reallocs,
+ "stack_alloc", self->data_stack_alloc,
+ "errors", self->stats.errors
+ );
+#else
+ return Py_BuildValue("");
+#endif /* COLLECT_STATS */
+}
+
+static PyMemberDef
+CTracer_members[] = {
+ { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0,
+ PyDoc_STR("Function indicating whether to trace a file.") },
+
+ { "warn", T_OBJECT, offsetof(CTracer, warn), 0,
+ PyDoc_STR("Function for issuing warnings.") },
+
+ { "data", T_OBJECT, offsetof(CTracer, data), 0,
+ PyDoc_STR("The raw dictionary of trace data.") },
+
+ { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
+ PyDoc_STR("Dictionary caching should_trace results.") },
+
+ { "arcs", T_OBJECT, offsetof(CTracer, arcs), 0,
+ PyDoc_STR("Should we trace arcs, or just lines?") },
+
+ { NULL }
+};
+
+static PyMethodDef
+CTracer_methods[] = {
+ { "start", (PyCFunction) CTracer_start, METH_VARARGS,
+ PyDoc_STR("Start the tracer") },
+
+ { "stop", (PyCFunction) CTracer_stop, METH_VARARGS,
+ PyDoc_STR("Stop the tracer") },
+
+ { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS,
+ PyDoc_STR("Get statistics about the tracing") },
+
+ { NULL }
+};
+
+static PyTypeObject
+CTracerType = {
+ MyType_HEAD_INIT
+ "coverage.CTracer", /*tp_name*/
+ sizeof(CTracer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CTracer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ (ternaryfunc)CTracer_call, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CTracer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ CTracer_methods, /* tp_methods */
+ CTracer_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)CTracer_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+/* Module definition */
+
+#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
+
+#if PY_MAJOR_VERSION >= 3
+
+static PyModuleDef
+moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "coverage.tracer",
+ MODULE_DOC,
+ -1,
+ NULL, /* methods */
+ NULL,
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL
+};
+
+
+PyObject *
+PyInit_tracer(void)
+{
+ PyObject * mod = PyModule_Create(&moduledef);
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+
+ Py_INCREF(&CTracerType);
+ PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
+
+ return mod;
+}
+
+#else
+
+void
+inittracer(void)
+{
+ PyObject * mod;
+
+ mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
+ if (mod == NULL) {
+ return;
+ }
+
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CTracerType);
+ PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
+}
+
+#endif /* Py3k */
diff --git a/python/helpers/coverage/tracer.pyd b/python/helpers/coverage/tracer.pyd
deleted file mode 100644
index a13aa03..0000000
--- a/python/helpers/coverage/tracer.pyd
+++ /dev/null
Binary files differ
diff --git a/python/helpers/coverage/version.py b/python/helpers/coverage/version.py
new file mode 100644
index 0000000..a43bde8
--- /dev/null
+++ b/python/helpers/coverage/version.py
@@ -0,0 +1,9 @@
+"""The version and URL for coverage.py"""
+# This file is exec'ed in setup.py, don't import anything!
+
+__version__ = "3.7.1" # see detailed history in CHANGES.txt
+
+__url__ = "http://nedbatchelder.com/code/coverage"
+if max(__version__).isalpha():
+ # For pre-releases, use a version-specific URL.
+ __url__ += "/" + __version__
diff --git a/python/helpers/coverage/xmlreport.py b/python/helpers/coverage/xmlreport.py
index 5f6cc87..26ac02a 100644
--- a/python/helpers/coverage/xmlreport.py
+++ b/python/helpers/coverage/xmlreport.py
@@ -4,7 +4,7 @@
import xml.dom.minidom
from coverage import __url__, __version__
-from coverage.backward import sorted # pylint: disable=W0622
+from coverage.backward import sorted, rpartition # pylint: disable=W0622
from coverage.report import Reporter
def rate(hit, num):
@@ -15,20 +15,19 @@
class XmlReporter(Reporter):
"""A reporter for writing Cobertura-style XML coverage results."""
- def __init__(self, coverage, ignore_errors=False):
- super(XmlReporter, self).__init__(coverage, ignore_errors)
+ def __init__(self, coverage, config):
+ super(XmlReporter, self).__init__(coverage, config)
self.packages = None
self.xml_out = None
self.arcs = coverage.data.has_arcs()
- def report(self, morfs, outfile=None, config=None):
+ def report(self, morfs, outfile=None):
"""Generate a Cobertura-compatible XML report for `morfs`.
`morfs` is a list of modules or filenames.
- `outfile` is a file object to write the XML to. `config` is a
- CoverageConfig instance.
+ `outfile` is a file object to write the XML to.
"""
# Initial setup.
@@ -54,7 +53,7 @@
# Call xml_file for each file in the data.
self.packages = {}
- self.report_files(self.xml_file, morfs, config)
+ self.report_files(self.xml_file, morfs)
lnum_tot, lhits_tot = 0, 0
bnum_tot, bhits_tot = 0, 0
@@ -85,14 +84,23 @@
# Use the DOM to write the output file.
outfile.write(self.xml_out.toprettyxml())
+ # Return the total percentage.
+ denom = lnum_tot + bnum_tot
+ if denom == 0:
+ pct = 0.0
+ else:
+ pct = 100.0 * (lhits_tot + bhits_tot) / denom
+ return pct
+
def xml_file(self, cu, analysis):
"""Add to the XML report for a single file."""
# Create the 'lines' and 'package' XML elements, which
# are populated later. Note that a package == a directory.
- dirname, fname = os.path.split(cu.name)
- dirname = dirname or '.'
- package = self.packages.setdefault(dirname, [ {}, 0, 0, 0, 0 ])
+ package_name = rpartition(cu.name, ".")[0]
+ className = cu.name
+
+ package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
xclass = self.xml_out.createElement("class")
@@ -100,22 +108,22 @@
xlines = self.xml_out.createElement("lines")
xclass.appendChild(xlines)
- className = fname.replace('.', '_')
+
xclass.setAttribute("name", className)
- ext = os.path.splitext(cu.filename)[1]
- xclass.setAttribute("filename", cu.name + ext)
+ filename = cu.file_locator.relative_filename(cu.filename)
+ xclass.setAttribute("filename", filename.replace("\\", "/"))
xclass.setAttribute("complexity", "0")
branch_stats = analysis.branch_stats()
# For each statement, create an XML 'line' element.
- for line in analysis.statements:
+ for line in sorted(analysis.statements):
xline = self.xml_out.createElement("line")
xline.setAttribute("number", str(line))
# Q: can we get info about the number of times a statement is
# executed? If so, that should be recorded here.
- xline.setAttribute("hits", str(int(not line in analysis.missing)))
+ xline.setAttribute("hits", str(int(line not in analysis.missing)))
if self.arcs:
if line in branch_stats:
diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py
index 50e9119..d09f6d9 100644
--- a/python/helpers/generator3.py
+++ b/python/helpers/generator3.py
@@ -161,6 +161,9 @@
path = os.path.normpath(path)
+ if path.endswith('.egg') and os.path.isfile(path):
+ say("%s\t%s\t%d", path, path, os.path.getsize(path))
+
for root, files in walk_python_path(path):
for name in files:
if name.endswith('.py'):
diff --git a/python/helpers/pep8.py b/python/helpers/pep8.py
index 2ce7554..f605f18 100644
--- a/python/helpers/pep8.py
+++ b/python/helpers/pep8.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# pep8.py - Check Python source code formatting, according to PEP 8
# Copyright (C) 2006-2009 Johann C. Rocholl <johann@rocholl.net>
-# Copyright (C) 2009-2013 Florent Xicluna <florent.xicluna@gmail.com>
+# Copyright (C) 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -24,8 +24,7 @@
# SOFTWARE.
r"""
-Check Python source code formatting, according to PEP 8:
-http://www.python.org/dev/peps/pep-0008/
+Check Python source code formatting, according to PEP 8.
For usage and a list of options, try this:
$ python pep8.py -h
@@ -45,7 +44,9 @@
700 statements
900 syntax error
"""
-__version__ = '1.4.5a0'
+from __future__ import with_statement
+
+__version__ = '1.5.7'
import os
import sys
@@ -63,13 +64,13 @@
from ConfigParser import RawConfigParser
DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__'
-DEFAULT_IGNORE = 'E226,E24'
+DEFAULT_IGNORE = 'E123,E226,E24'
if sys.platform == 'win32':
DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8')
else:
DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or
os.path.expanduser('~/.config'), 'pep8')
-PROJECT_CONFIG = ('.pep8', 'tox.ini', 'setup.cfg')
+PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8')
TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
MAX_LINE_LENGTH = 79
REPORT_FORMAT = {
@@ -87,18 +88,21 @@
'**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>',
'%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '='])
WHITESPACE = frozenset(' \t')
-SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE,
- tokenize.INDENT, tokenize.DEDENT])
+NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
+SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT])
+# ERRORTOKEN is triggered by backticks in Python 3
+SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN])
BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines']
INDENT_REGEX = re.compile(r'([ \t]*)')
-RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)')
-RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+')
+RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
+RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
DOCSTRING_REGEX = re.compile(r'u?r?["\']')
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)')
+COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^[({ ]+\s+(in|is)\s')
COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type'
r'|\s*\(\s*([^)]*[^ )])\s*\))')
KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS))
@@ -117,8 +121,7 @@
def tabs_or_spaces(physical_line, indent_char):
- r"""
- Never mix tabs and spaces.
+ r"""Never mix tabs and spaces.
The most popular way of indenting Python is with spaces only. The
second-most popular way is with tabs only. Code indented with a mixture
@@ -137,9 +140,7 @@
def tabs_obsolete(physical_line):
- r"""
- For new projects, spaces-only are strongly recommended over tabs. Most
- editors have features that make this easy to do.
+ r"""For new projects, spaces-only are strongly recommended over tabs.
Okay: if True:\n return
W191: if True:\n\treturn
@@ -150,16 +151,7 @@
def trailing_whitespace(physical_line):
- r"""
- JCR: Trailing whitespace is superfluous.
- FBM: Except when it occurs as part of a blank line (i.e. the line is
- nothing but whitespace). According to Python docs[1] a line with only
- whitespace is considered a blank line, and is to be ignored. However,
- matching a blank line to its indentation level avoids mistakenly
- terminating a multi-line statement (e.g. class declaration) when
- pasting code into the standard Python interpreter.
-
- [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines
+ r"""Trailing whitespace is superfluous.
The warning returned varies on whether the line itself is blank, for easier
filtering for those who want to indent their blank lines.
@@ -179,30 +171,24 @@
return 0, "W293 blank line contains whitespace"
-def trailing_blank_lines(physical_line, lines, line_number):
- r"""
- JCR: Trailing blank lines are superfluous.
+def trailing_blank_lines(physical_line, lines, line_number, total_lines):
+ r"""Trailing blank lines are superfluous.
Okay: spam(1)
W391: spam(1)\n
+
+ However the last line should end with a new line (warning W292).
"""
- if not physical_line.rstrip() and line_number == len(lines):
- return 0, "W391 blank line at end of file"
+ if line_number == total_lines:
+ stripped_last_line = physical_line.rstrip()
+ if not stripped_last_line:
+ return 0, "W391 blank line at end of file"
+ if stripped_last_line == physical_line:
+ return len(physical_line), "W292 no newline at end of file"
-def missing_newline(physical_line):
- """
- JCR: The last line should have a newline.
-
- Reports warning W292.
- """
- if physical_line.rstrip() == physical_line:
- return len(physical_line), "W292 no newline at end of file"
-
-
-def maximum_line_length(physical_line, max_line_length):
- """
- Limit all lines to a maximum of 79 characters.
+def maximum_line_length(physical_line, max_line_length, multiline):
+ r"""Limit all lines to a maximum of 79 characters.
There are still many devices around that are limited to 80 character
lines; plus, limiting windows to 80 characters makes it possible to have
@@ -215,8 +201,13 @@
"""
line = physical_line.rstrip()
length = len(line)
- if length > max_line_length:
- if noqa(line):
+ if length > max_line_length and not noqa(line):
+ # Special case for long URLs in multi-line docstrings or comments,
+ # but still report the error when the 72 first chars are whitespaces.
+ chunks = line.split()
+ if ((len(chunks) == 1 and multiline) or
+ (len(chunks) == 2 and chunks[0] == '#')) and \
+ len(line) - len(chunks[-1]) < max_line_length - 7:
return
if hasattr(line, 'decode'): # Python 2
# The line could contain multi-byte characters
@@ -235,9 +226,8 @@
def blank_lines(logical_line, blank_lines, indent_level, line_number,
- previous_logical, previous_indent_level):
- r"""
- Separate top-level function and class definitions with two blank lines.
+ blank_before, previous_logical, previous_indent_level):
+ r"""Separate top-level function and class definitions with two blank lines.
Method definitions inside a class are separated by a single blank line.
@@ -265,19 +255,18 @@
yield 0, "E303 too many blank lines (%d)" % blank_lines
elif logical_line.startswith(('def ', 'class ', '@')):
if indent_level:
- if not (blank_lines or previous_indent_level < indent_level or
+ if not (blank_before or previous_indent_level < indent_level or
DOCSTRING_REGEX.match(previous_logical)):
yield 0, "E301 expected 1 blank line, found 0"
- elif blank_lines != 2:
- yield 0, "E302 expected 2 blank lines, found %d" % blank_lines
+ elif blank_before != 2:
+ yield 0, "E302 expected 2 blank lines, found %d" % blank_before
def extraneous_whitespace(logical_line):
- """
- Avoid extraneous whitespace in the following situations:
+ r"""Avoid extraneous whitespace.
+ Avoid extraneous whitespace in these situations:
- Immediately inside parentheses, brackets or braces.
-
- Immediately before a comma, semicolon, or colon.
Okay: spam(ham[1], {eggs: 2})
@@ -306,8 +295,7 @@
def whitespace_around_keywords(logical_line):
- r"""
- Avoid extraneous whitespace around keywords.
+ r"""Avoid extraneous whitespace around keywords.
Okay: True and False
E271: True and False
@@ -330,8 +318,7 @@
def missing_whitespace(logical_line):
- """
- JCR: Each comma, semicolon or colon should be followed by whitespace.
+ r"""Each comma, semicolon or colon should be followed by whitespace.
Okay: [a, b]
Okay: (3,)
@@ -358,8 +345,7 @@
def indentation(logical_line, previous_logical, indent_char,
indent_level, previous_indent_level):
- r"""
- Use 4 spaces per indentation level.
+ r"""Use 4 spaces per indentation level.
For really old code that you don't want to mess up, you can continue to
use 8-space tabs.
@@ -383,16 +369,16 @@
yield 0, "E113 unexpected indentation"
-def continuation_line_indentation(logical_line, tokens, indent_level, verbose):
- r"""
- Continuation lines should align wrapped elements either vertically using
- Python's implicit line joining inside parentheses, brackets and braces, or
- using a hanging indent.
+def continued_indentation(logical_line, tokens, indent_level, hang_closing,
+ indent_char, noqa, verbose):
+ r"""Continuation lines indentation.
- When using a hanging indent the following considerations should be applied:
+ Continuation lines should align wrapped elements either vertically
+ using Python's implicit line joining inside parentheses, brackets
+ and braces, or using a hanging indent.
+ When using a hanging indent these considerations should be applied:
- there should be no arguments on the first line, and
-
- further indentation should be used to clearly distinguish itself as a
continuation line.
@@ -404,14 +390,16 @@
E122: a = (\n42)
E123: a = (\n 42\n )
E124: a = (24,\n 42\n)
- E125: if (a or\n b):\n pass
+ E125: if (\n b):\n pass
E126: a = (\n 42)
E127: a = (24,\n 42)
E128: a = (24,\n 42)
+ E129: if (a or\n b):\n pass
+ E131: a = (\n 42\n 24)
"""
first_row = tokens[0][2][0]
nrows = 1 + tokens[-1][2][0] - first_row
- if nrows == 1 or noqa(tokens[0][4]):
+ if noqa or nrows == 1:
return
# indent_next tells us whether the next block is indented; assuming
@@ -421,13 +409,20 @@
indent_next = logical_line.endswith(':')
row = depth = 0
+ valid_hangs = (4,) if indent_char != '\t' else (4, 8)
# remember how many brackets were opened on each line
parens = [0] * nrows
# relative indents of physical lines
rel_indent = [0] * nrows
+ # for each depth, collect a list of opening rows
+ open_rows = [[0]]
+ # for each depth, memorize the hanging indentation
+ hangs = [None]
# visual indents
indent_chances = {}
last_indent = tokens[0][2]
+ visual_indent = None
+ # for each depth, memorize the visual indent column
indent = [last_indent[1]]
if verbose >= 3:
print(">>> " + tokens[0][4].rstrip())
@@ -437,8 +432,7 @@
newline = row < start[0] - first_row
if newline:
row = start[0] - first_row
- newline = (not last_token_multiline and
- token_type not in (tokenize.NL, tokenize.NEWLINE))
+ newline = not last_token_multiline and token_type not in NEWLINE
if newline:
# this is the beginning of a continuation line.
@@ -449,51 +443,61 @@
# record the initial indent.
rel_indent[row] = expand_indent(line) - indent_level
- if depth:
- # a bracket expression in a continuation line.
- # find the line that it was opened on
- for open_row in range(row - 1, -1, -1):
- if parens[open_row]:
- break
- else:
- # an unbracketed continuation line (ie, backslash)
- open_row = 0
- hang = rel_indent[row] - rel_indent[open_row]
- visual_indent = indent_chances.get(start[1])
+ # identify closing bracket
+ close_bracket = (token_type == tokenize.OP and text in ']})')
- if token_type == tokenize.OP and text in ']})':
- # this line starts with a closing bracket
- if indent[depth]:
- if start[1] != indent[depth]:
- yield (start, "E124 closing bracket does not match "
- "visual indentation")
- elif hang:
+ # is the indent relative to an opening bracket line?
+ for open_row in reversed(open_rows[depth]):
+ hang = rel_indent[row] - rel_indent[open_row]
+ hanging_indent = hang in valid_hangs
+ if hanging_indent:
+ break
+ if hangs[depth]:
+ hanging_indent = (hang == hangs[depth])
+ # is there any chance of visual indent?
+ visual_indent = (not close_bracket and hang > 0 and
+ indent_chances.get(start[1]))
+
+ if close_bracket and indent[depth]:
+ # closing bracket for visual indent
+ if start[1] != indent[depth]:
+ yield (start, "E124 closing bracket does not match "
+ "visual indentation")
+ elif close_bracket and not hang:
+ # closing bracket matches indentation of opening bracket's line
+ if hang_closing:
+ yield start, "E133 closing bracket is missing indentation"
+ elif indent[depth] and start[1] < indent[depth]:
+ if visual_indent is not True:
+ # visual indent is broken
+ yield (start, "E128 continuation line "
+ "under-indented for visual indent")
+ elif hanging_indent or (indent_next and rel_indent[row] == 8):
+ # hanging indent is verified
+ if close_bracket and not hang_closing:
yield (start, "E123 closing bracket does not match "
"indentation of opening bracket's line")
+ hangs[depth] = hang
elif visual_indent is True:
# visual indent is verified
- if not indent[depth]:
- indent[depth] = start[1]
+ indent[depth] = start[1]
elif visual_indent in (text, str):
# ignore token lined up with matching one from a previous line
pass
- elif indent[depth] and start[1] < indent[depth]:
- # visual indent is broken
- yield (start, "E128 continuation line "
- "under-indented for visual indent")
- elif hang == 4 or (indent_next and rel_indent[row] == 8):
- # hanging indent is verified
- pass
else:
# indent is broken
if hang <= 0:
error = "E122", "missing indentation or outdented"
elif indent[depth]:
error = "E127", "over-indented for visual indent"
- elif hang % 4:
- error = "E121", "indentation is not a multiple of four"
+ elif not close_bracket and hangs[depth]:
+ error = "E131", "unaligned for hanging indent"
else:
- error = "E126", "over-indented for hanging indent"
+ hangs[depth] = hang
+ if hang > 4:
+ error = "E126", "over-indented for hanging indent"
+ else:
+ error = "E121", "under-indented for hanging indent"
yield start, "%s continuation line %s" % error
# look for visual indenting
@@ -510,12 +514,18 @@
# special case for the "if" statement because len("if (") == 4
elif not indent_chances and not row and not depth and text == 'if':
indent_chances[end[1] + 1] = True
+ elif text == ':' and line[end[1]:].isspace():
+ open_rows[depth].append(row)
# keep track of bracket depth
if token_type == tokenize.OP:
if text in '([{':
depth += 1
indent.append(0)
+ hangs.append(None)
+ if len(open_rows) == depth:
+ open_rows.append([])
+ open_rows[depth].append(row)
parens[row] += 1
if verbose >= 4:
print("bracket depth %s seen, col %s, visual min = %s" %
@@ -523,12 +533,14 @@
elif text in ')]}' and depth > 0:
# parent indents should not be more than this one
prev_indent = indent.pop() or last_indent[1]
+ hangs.pop()
for d in range(depth):
if indent[d] > prev_indent:
indent[d] = 0
for ind in list(indent_chances):
if ind >= prev_indent:
del indent_chances[ind]
+ del open_rows[depth + 1:]
depth -= 1
if depth:
indent_chances[indent[depth]] = True
@@ -542,21 +554,25 @@
indent_chances[start[1]] = text
last_token_multiline = (start[0] != end[0])
+ if last_token_multiline:
+ rel_indent[end[0] - first_row] = rel_indent[row]
- if indent_next and rel_indent[-1] == 4:
- yield (last_indent, "E125 continuation line does not distinguish "
- "itself from next logical line")
+ if indent_next and expand_indent(line) == indent_level + 4:
+ pos = (start[0], indent[0] + 4)
+ if visual_indent:
+ code = "E129 visually indented line"
+ else:
+ code = "E125 continuation line"
+ yield pos, "%s with same indent as next logical line" % code
def whitespace_before_parameters(logical_line, tokens):
- """
+ r"""Avoid extraneous whitespace.
+
Avoid extraneous whitespace in the following situations:
-
- - Immediately before the open parenthesis that starts the argument
- list of a function call.
-
- - Immediately before the open parenthesis that starts an indexing or
- slicing.
+ - before the open parenthesis that starts the argument list of a
+ function call.
+ - before the open parenthesis that starts an indexing or slicing.
Okay: spam(1)
E211: spam (1)
@@ -565,11 +581,9 @@
E211: dict ['key'] = list[index]
E211: dict['key'] = list [index]
"""
- prev_type = tokens[0][0]
- prev_text = tokens[0][1]
- prev_end = tokens[0][3]
+ prev_type, prev_text, __, prev_end, __ = tokens[0]
for index in range(1, len(tokens)):
- token_type, text, start, end, line = tokens[index]
+ token_type, text, start, end, __ = tokens[index]
if (token_type == tokenize.OP and
text in '([' and
start != prev_end and
@@ -585,11 +599,7 @@
def whitespace_around_operator(logical_line):
- r"""
- Avoid extraneous whitespace in the following situations:
-
- - More than one space around an assignment (or other) operator to
- align it with another.
+ r"""Avoid extraneous whitespace around an operator.
Okay: a = 12 + 3
E221: a = 4 + 5
@@ -612,13 +622,15 @@
def missing_whitespace_around_operator(logical_line, tokens):
- r"""
+ r"""Surround operators with a single space on either side.
+
- Always surround these binary operators with a single space on
either side: assignment (=), augmented assignment (+=, -= etc.),
- comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not),
+ comparisons (==, <, >, !=, <=, >=, in, not in, is, is not),
Booleans (and, or, not).
- - Use spaces around arithmetic operators.
+ - If operators with different priorities are used, consider adding
+ whitespace around the operators with the lowest priorities.
Okay: i = i + 1
Okay: submitted += 1
@@ -642,8 +654,7 @@
prev_type = tokenize.OP
prev_text = prev_end = None
for token_type, text, start, end, line in tokens:
- if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN):
- # ERRORTOKEN is triggered by backticks in Python 3
+ if token_type in SKIP_COMMENTS:
continue
if text in ('(', 'lambda'):
parens += 1
@@ -683,18 +694,9 @@
# Check if the operator is being used as a binary operator
# Allow unary operators: -123, -x, +1.
# Allow argument unpacking: foo(*args, **kwargs).
- if prev_type == tokenize.OP:
- binary_usage = (prev_text in '}])')
- elif prev_type == tokenize.NAME:
- binary_usage = (prev_text not in KEYWORDS)
- else:
- binary_usage = (prev_type not in SKIP_TOKENS)
-
- if binary_usage:
- if text in WS_OPTIONAL_OPERATORS:
- need_space = None
- else:
- need_space = True
+ if (prev_text in '}])' if prev_type == tokenize.OP
+ else prev_text not in KEYWORDS):
+ need_space = None
elif text in WS_OPTIONAL_OPERATORS:
need_space = None
@@ -712,11 +714,7 @@
def whitespace_around_comma(logical_line):
- r"""
- Avoid extraneous whitespace in the following situations:
-
- - More than one space around an assignment (or other) operator to
- align it with another.
+ r"""Avoid extraneous whitespace after a comma or a colon.
Note: these checks are disabled by default
@@ -734,7 +732,8 @@
def whitespace_around_named_parameter_equals(logical_line, tokens):
- """
+ r"""Don't use spaces around the '=' sign in function arguments.
+
Don't use spaces around the '=' sign when used to indicate a
keyword argument or a default parameter value.
@@ -753,6 +752,8 @@
prev_end = None
message = "E251 unexpected spaces around keyword / parameter equals"
for token_type, text, start, end, line in tokens:
+ if token_type == tokenize.NL:
+ continue
if no_space:
no_space = False
if start != prev_end:
@@ -769,38 +770,46 @@
prev_end = end
-def whitespace_before_inline_comment(logical_line, tokens):
- """
- Separate inline comments by at least two spaces.
+def whitespace_before_comment(logical_line, tokens):
+ r"""Separate inline comments by at least two spaces.
An inline comment is a comment on the same line as a statement. Inline
comments should be separated by at least two spaces from the statement.
They should start with a # and a single space.
+ Each line of a block comment starts with a # and a single space
+ (unless it is indented text inside the comment).
+
Okay: x = x + 1 # Increment x
Okay: x = x + 1 # Increment x
+ Okay: # Block comment
E261: x = x + 1 # Increment x
E262: x = x + 1 #Increment x
E262: x = x + 1 # Increment x
+ E265: #Block comment
"""
prev_end = (0, 0)
for token_type, text, start, end, line in tokens:
if token_type == tokenize.COMMENT:
- if not line[:start[1]].strip():
- continue
- if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
- yield (prev_end,
- "E261 at least two spaces before inline comment")
+ inline_comment = line[:start[1]].strip()
+ if inline_comment:
+ if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
+ yield (prev_end,
+ "E261 at least two spaces before inline comment")
symbol, sp, comment = text.partition(' ')
- if symbol not in ('#', '#:') or comment[:1].isspace():
- yield start, "E262 inline comment should start with '# '"
+ bad_prefix = symbol not in ('#', '#:')
+ if inline_comment:
+ if bad_prefix or comment[:1].isspace():
+ yield start, "E262 inline comment should start with '# '"
+ elif bad_prefix:
+ if text.rstrip('#') and (start[0] > 1 or symbol[1] != '!'):
+ yield start, "E265 block comment should start with '# '"
elif token_type != tokenize.NL:
prev_end = end
def imports_on_separate_lines(logical_line):
- r"""
- Imports should usually be on separate lines.
+ r"""Imports should usually be on separate lines.
Okay: import os\nimport sys
E401: import sys, os
@@ -819,13 +828,11 @@
def compound_statements(logical_line):
- r"""
- Compound statements (multiple statements on the same line) are
- generally discouraged.
+ r"""Compound statements (on the same line) are generally discouraged.
While sometimes it's okay to put an if/for/while with a small body
- on the same line, never do this for multi-clause statements. Also
- avoid folding such long lines!
+ on the same line, never do this for multi-clause statements.
+ Also avoid folding such long lines!
Okay: if foo == 'blah':\n do_blah_thing()
Okay: do_one()
@@ -847,24 +854,25 @@
line = logical_line
last_char = len(line) - 1
found = line.find(':')
- if -1 < found < last_char:
+ while -1 < found < last_char:
before = line[:found]
if (before.count('{') <= before.count('}') and # {'a': 1} (dict)
before.count('[') <= before.count(']') and # [1:2] (slice)
before.count('(') <= before.count(')') and # (Python 3 annotation)
not LAMBDA_REGEX.search(before)): # lambda x: x
yield found, "E701 multiple statements on one line (colon)"
+ found = line.find(':', found + 1)
found = line.find(';')
- if -1 < found:
+ while -1 < found:
if found < last_char:
yield found, "E702 multiple statements on one line (semicolon)"
else:
yield found, "E703 statement ends with a semicolon"
+ found = line.find(';', found + 1)
def explicit_line_join(logical_line, tokens):
- r"""
- Avoid explicit line join between brackets.
+ r"""Avoid explicit line join between brackets.
The preferred way of wrapping long lines is by using Python's implied line
continuation inside parentheses, brackets and braces. Long lines can be
@@ -897,8 +905,9 @@
parens -= 1
-def comparison_to_singleton(logical_line):
- """
+def comparison_to_singleton(logical_line, noqa):
+ r"""Comparison to singletons should use "is" or "is not".
+
Comparisons to singletons like None should always be done
with "is" or "is not", never the equality operators.
@@ -911,7 +920,7 @@
set to some other value. The other value might have a type (such as a
container) that could be false in a boolean context!
"""
- match = COMPARE_SINGLETON_REGEX.search(logical_line)
+ match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line)
if match:
same = (match.group(1) == '==')
singleton = match.group(2)
@@ -927,10 +936,31 @@
(code, singleton, msg))
-def comparison_type(logical_line):
+def comparison_negative(logical_line):
+ r"""Negative comparison should be done using "not in" and "is not".
+
+ Okay: if x not in y:\n pass
+ Okay: assert (X in Y or X is Z)
+ Okay: if not (X in Y):\n pass
+ Okay: zz = x is not y
+ E713: Z = not X in Y
+ E713: if not X.B in Y:\n pass
+ E714: if not X is Y:\n pass
+ E714: Z = not X.B is Y
"""
- Object type comparisons should always use isinstance() instead of
- comparing types directly.
+ match = COMPARE_NEGATIVE_REGEX.search(logical_line)
+ if match:
+ pos = match.start(1)
+ if match.group(2) == 'in':
+ yield pos, "E713 test for membership should be 'not in'"
+ else:
+ yield pos, "E714 test for object identity should be 'is not'"
+
+
+def comparison_type(logical_line):
+ r"""Object type comparisons should always use isinstance().
+
+ Do not compare types directly.
Okay: if isinstance(obj, int):
E721: if type(obj) is type(1):
@@ -947,44 +977,36 @@
inst = match.group(1)
if inst and isidentifier(inst) and inst not in SINGLETONS:
return # Allow comparison for types which are not obvious
- yield match.start(0), "E721 do not compare types, use 'isinstance()'"
+ yield match.start(), "E721 do not compare types, use 'isinstance()'"
-def python_3000_has_key(logical_line):
- r"""
- The {}.has_key() method is removed in the Python 3.
- Use the 'in' operation instead.
+def python_3000_has_key(logical_line, noqa):
+ r"""The {}.has_key() method is removed in Python 3: use the 'in' operator.
Okay: if "alph" in d:\n print d["alph"]
W601: assert d.has_key('alph')
"""
pos = logical_line.find('.has_key(')
- if pos > -1:
+ if pos > -1 and not noqa:
yield pos, "W601 .has_key() is deprecated, use 'in'"
def python_3000_raise_comma(logical_line):
- """
- When raising an exception, use "raise ValueError('message')"
- instead of the older form "raise ValueError, 'message'".
+ r"""When raising an exception, use "raise ValueError('message')".
- The paren-using form is preferred because when the exception arguments
- are long or include string formatting, you don't need to use line
- continuation characters thanks to the containing parentheses. The older
- form is removed in Python 3.
+ The older form is removed in Python 3.
Okay: raise DummyError("Message")
W602: raise DummyError, "Message"
"""
match = RAISE_COMMA_REGEX.match(logical_line)
if match and not RERAISE_COMMA_REGEX.match(logical_line):
- yield match.start(1), "W602 deprecated form of raising exception"
+ yield match.end() - 1, "W602 deprecated form of raising exception"
def python_3000_not_equal(logical_line):
- """
- != can also be written <>, but this is an obsolete usage kept for
- backwards compatibility only. New code should always use !=.
+ r"""New code should always use != instead of <>.
+
The older syntax is removed in Python 3.
Okay: if a != 'no':
@@ -996,9 +1018,7 @@
def python_3000_backticks(logical_line):
- """
- Backticks are removed in Python 3.
- Use repr() instead.
+ r"""Backticks are removed in Python 3: use repr() instead.
Okay: val = repr(1 + 2)
W604: val = `1 + 2`
@@ -1016,49 +1036,40 @@
if '' == ''.encode():
# Python 2: implicit encoding.
def readlines(filename):
- f = open(filename)
- try:
+ """Read the source code."""
+ with open(filename, 'rU') as f:
return f.readlines()
- finally:
- f.close()
-
isidentifier = re.compile(r'[a-zA-Z_]\w*').match
stdin_get_value = sys.stdin.read
else:
# Python 3
def readlines(filename):
- f = open(filename, 'rb')
+ """Read the source code."""
try:
- coding, lines = tokenize.detect_encoding(f.readline)
- f = TextIOWrapper(f, coding, line_buffering=True)
- return [l.decode(coding) for l in lines] + f.readlines()
+ with open(filename, 'rb') as f:
+ (coding, lines) = tokenize.detect_encoding(f.readline)
+ f = TextIOWrapper(f, coding, line_buffering=True)
+ return [l.decode(coding) for l in lines] + f.readlines()
except (LookupError, SyntaxError, UnicodeError):
- f.close()
- # Fall back if files are improperly declared
- f = open(filename, encoding='latin-1')
- return f.readlines()
- finally:
- f.close()
-
+ # Fall back if file encoding is improperly declared
+ with open(filename, encoding='latin-1') as f:
+ return f.readlines()
isidentifier = str.isidentifier
def stdin_get_value():
return TextIOWrapper(sys.stdin.buffer, errors='ignore').read()
-readlines.__doc__ = " Read the source code."
noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search
def expand_indent(line):
- r"""
- Return the amount of indentation.
+ r"""Return the amount of indentation.
+
Tabs are expanded to the next multiple of 8.
>>> expand_indent(' ')
4
>>> expand_indent('\t')
8
- >>> expand_indent(' \t')
- 8
>>> expand_indent(' \t')
8
>>> expand_indent(' \t')
@@ -1078,8 +1089,7 @@
def mute_string(text):
- """
- Replace contents with 'xxx' to prevent syntax matching.
+ """Replace contents with 'xxx' to prevent syntax matching.
>>> mute_string('"abc"')
'"xxx"'
@@ -1111,7 +1121,7 @@
continue
if line[:3] == '@@ ':
hunk_match = HUNK_REGEX.match(line)
- row, nrows = [int(g or '1') for g in hunk_match.groups()]
+ (row, nrows) = [int(g or '1') for g in hunk_match.groups()]
rv[path].update(range(row, row + nrows))
elif line[:3] == '+++':
path = line[4:].split('\t', 1)[0]
@@ -1123,9 +1133,24 @@
if rows and filename_match(path, patterns)])
-def filename_match(filename, patterns, default=True):
+def normalize_paths(value, parent=os.curdir):
+ """Parse a comma-separated list of paths.
+
+ Return a list of absolute paths.
"""
- Check if patterns contains a pattern that matches filename.
+ if not value or isinstance(value, list):
+ return value
+ paths = []
+ for path in value.split(','):
+ if '/' in path:
+ path = os.path.abspath(os.path.join(parent, path))
+ paths.append(path.rstrip('/'))
+ return paths
+
+
+def filename_match(filename, patterns, default=True):
+ """Check if patterns contains a pattern that matches filename.
+
If patterns is unspecified, this always returns True.
"""
if not patterns:
@@ -1133,6 +1158,15 @@
return any(fnmatch(filename, pattern) for pattern in patterns)
+if COMMENT_WITH_NL:
+ def _is_eol_token(token):
+ return (token[0] in NEWLINE or
+ (token[0] == tokenize.COMMENT and token[1] == token[4]))
+else:
+ def _is_eol_token(token):
+ return token[0] in NEWLINE
+
+
##############################################################################
# Framework to run all checks
##############################################################################
@@ -1142,9 +1176,7 @@
def register_check(check, codes=None):
- """
- Register a new check object.
- """
+ """Register a new check object."""
def _add_check(check, kind, codes, args):
if check in _checks[kind]:
_checks[kind][check][0].extend(codes or [])
@@ -1162,9 +1194,9 @@
def init_checks_registry():
- """
- Register all globally visible functions where the first argument name
- is 'physical_line' or 'logical_line'.
+ """Register all globally visible functions.
+
+ The first argument name is either 'physical_line' or 'logical_line'.
"""
mod = inspect.getmodule(register_check)
for (name, function) in inspect.getmembers(mod, inspect.isfunction):
@@ -1173,9 +1205,7 @@
class Checker(object):
- """
- Load a Python source file, tokenize it, check coding style.
- """
+ """Load a Python source file, tokenize it, check coding style."""
def __init__(self, filename=None, lines=None,
options=None, report=None, **kwargs):
@@ -1188,6 +1218,8 @@
self._logical_checks = options.logical_checks
self._ast_checks = options.ast_checks
self.max_line_length = options.max_line_length
+ self.multiline = False # in a multiline string?
+ self.hang_closing = options.hang_closing
self.verbose = options.verbose
self.filename = filename
if filename is None:
@@ -1200,161 +1232,190 @@
try:
self.lines = readlines(filename)
except IOError:
- exc_type, exc = sys.exc_info()[:2]
+ (exc_type, exc) = sys.exc_info()[:2]
self._io_error = '%s: %s' % (exc_type.__name__, exc)
self.lines = []
else:
self.lines = lines
+ if self.lines:
+ ord0 = ord(self.lines[0][0])
+ if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM
+ if ord0 == 0xfeff:
+ self.lines[0] = self.lines[0][1:]
+ elif self.lines[0][:3] == '\xef\xbb\xbf':
+ self.lines[0] = self.lines[0][3:]
self.report = report or options.report
self.report_error = self.report.error
def report_invalid_syntax(self):
- exc_type, exc = sys.exc_info()[:2]
- offset = exc.args[1]
- if len(offset) > 2:
- offset = offset[1:3]
+ """Check if the syntax is valid."""
+ (exc_type, exc) = sys.exc_info()[:2]
+ if len(exc.args) > 1:
+ offset = exc.args[1]
+ if len(offset) > 2:
+ offset = offset[1:3]
+ else:
+ offset = (1, 0)
self.report_error(offset[0], offset[1] or 0,
'E901 %s: %s' % (exc_type.__name__, exc.args[0]),
self.report_invalid_syntax)
- report_invalid_syntax.__doc__ = " Check if the syntax is valid."
def readline(self):
- """
- Get the next line from the input buffer.
- """
- self.line_number += 1
- if self.line_number > len(self.lines):
+ """Get the next line from the input buffer."""
+ if self.line_number >= self.total_lines:
return ''
- return self.lines[self.line_number - 1]
-
- def readline_check_physical(self):
- """
- Check and return the next physical line. This method can be
- used to feed tokenize.generate_tokens.
- """
- line = self.readline()
- if line:
- self.check_physical(line)
+ line = self.lines[self.line_number]
+ self.line_number += 1
+ if self.indent_char is None and line[:1] in WHITESPACE:
+ self.indent_char = line[0]
return line
def run_check(self, check, argument_names):
- """
- Run a check plugin.
- """
+ """Run a check plugin."""
arguments = []
for name in argument_names:
arguments.append(getattr(self, name))
return check(*arguments)
def check_physical(self, line):
- """
- Run all physical checks on a raw input line.
- """
+ """Run all physical checks on a raw input line."""
self.physical_line = line
- if self.indent_char is None and line[:1] in WHITESPACE:
- self.indent_char = line[0]
for name, check, argument_names in self._physical_checks:
result = self.run_check(check, argument_names)
if result is not None:
- offset, text = result
+ (offset, text) = result
self.report_error(self.line_number, offset, text, check)
+ if text[:4] == 'E101':
+ self.indent_char = line[0]
def build_tokens_line(self):
- """
- Build a logical line from tokens.
- """
- self.mapping = []
+ """Build a logical line from tokens."""
logical = []
+ comments = []
length = 0
- previous = None
- for token in self.tokens:
- token_type, text = token[0:2]
+ prev_row = prev_col = mapping = None
+ for token_type, text, start, end, line in self.tokens:
if token_type in SKIP_TOKENS:
continue
+ if not mapping:
+ mapping = [(0, start)]
+ if token_type == tokenize.COMMENT:
+ comments.append(text)
+ continue
if token_type == tokenize.STRING:
text = mute_string(text)
- if previous:
- end_row, end = previous[3]
- start_row, start = token[2]
- if end_row != start_row: # different row
- prev_text = self.lines[end_row - 1][end - 1]
+ if prev_row:
+ (start_row, start_col) = start
+ if prev_row != start_row: # different row
+ prev_text = self.lines[prev_row - 1][prev_col - 1]
if prev_text == ',' or (prev_text not in '{[('
and text not in '}])'):
- logical.append(' ')
- length += 1
- elif end != start: # different column
- fill = self.lines[end_row - 1][end:start]
- logical.append(fill)
- length += len(fill)
- self.mapping.append((length, token))
+ text = ' ' + text
+ elif prev_col != start_col: # different column
+ text = line[prev_col:start_col] + text
logical.append(text)
length += len(text)
- previous = token
+ mapping.append((length, end))
+ (prev_row, prev_col) = end
self.logical_line = ''.join(logical)
- # With Python 2, if the line ends with '\r\r\n' the assertion fails
- # assert self.logical_line.strip() == self.logical_line
+ self.noqa = comments and noqa(''.join(comments))
+ return mapping
def check_logical(self):
- """
- Build a line from tokens and run all logical checks on it.
- """
- self.build_tokens_line()
+ """Build a line from tokens and run all logical checks on it."""
self.report.increment_logical_line()
- first_line = self.lines[self.mapping[0][1][2][0] - 1]
- indent = first_line[:self.mapping[0][1][2][1]]
- self.previous_indent_level = self.indent_level
- self.indent_level = expand_indent(indent)
+ mapping = self.build_tokens_line()
+ (start_row, start_col) = mapping[0][1]
+ start_line = self.lines[start_row - 1]
+ self.indent_level = expand_indent(start_line[:start_col])
+ if self.blank_before < self.blank_lines:
+ self.blank_before = self.blank_lines
if self.verbose >= 2:
print(self.logical_line[:80].rstrip())
for name, check, argument_names in self._logical_checks:
if self.verbose >= 4:
print(' ' + name)
- for result in self.run_check(check, argument_names):
- offset, text = result
- if isinstance(offset, tuple):
- orig_number, orig_offset = offset
- else:
- for token_offset, token in self.mapping:
- if offset >= token_offset:
- orig_number = token[2][0]
- orig_offset = (token[2][1] + offset - token_offset)
- self.report_error(orig_number, orig_offset, text, check)
- self.previous_logical = self.logical_line
+ for offset, text in self.run_check(check, argument_names) or ():
+ if not isinstance(offset, tuple):
+ for token_offset, pos in mapping:
+ if offset <= token_offset:
+ break
+ offset = (pos[0], pos[1] + offset - token_offset)
+ self.report_error(offset[0], offset[1], text, check)
+ if self.logical_line:
+ self.previous_indent_level = self.indent_level
+ self.previous_logical = self.logical_line
+ self.blank_lines = 0
+ self.tokens = []
def check_ast(self):
+ """Build the file's AST and run all AST checks."""
try:
tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
- except SyntaxError:
+ except (SyntaxError, TypeError):
return self.report_invalid_syntax()
- for name, cls, _ in self._ast_checks:
+ for name, cls, __ in self._ast_checks:
checker = cls(tree, self.filename)
for lineno, offset, text, check in checker.run():
- if not noqa(self.lines[lineno - 1]):
+ if not self.lines or not noqa(self.lines[lineno - 1]):
self.report_error(lineno, offset, text, check)
def generate_tokens(self):
+ """Tokenize the file, run physical line checks and yield tokens."""
if self._io_error:
self.report_error(1, 0, 'E902 %s' % self._io_error, readlines)
- tokengen = tokenize.generate_tokens(self.readline_check_physical)
+ tokengen = tokenize.generate_tokens(self.readline)
try:
for token in tokengen:
+ if token[2][0] > self.total_lines:
+ return
+ self.maybe_check_physical(token)
yield token
except (SyntaxError, tokenize.TokenError):
self.report_invalid_syntax()
+ def maybe_check_physical(self, token):
+ """If appropriate (based on token), check current physical line(s)."""
+ # Called after every token, but act only on end of line.
+ if _is_eol_token(token):
+ # Obviously, a newline token ends a single physical line.
+ self.check_physical(token[4])
+ elif token[0] == tokenize.STRING and '\n' in token[1]:
+ # Less obviously, a string that contains newlines is a
+ # multiline string, either triple-quoted or with internal
+ # newlines backslash-escaped. Check every physical line in the
+ # string *except* for the last one: its newline is outside of
+ # the multiline string, so we consider it a regular physical
+ # line, and will check it like any other physical line.
+ #
+ # Subtleties:
+ # - we don't *completely* ignore the last line; if it contains
+ # the magical "# noqa" comment, we disable all physical
+ # checks for the entire multiline string
+ # - have to wind self.line_number back because initially it
+ # points to the last line of the string, and we want
+ # check_physical() to give accurate feedback
+ if noqa(token[4]):
+ return
+ self.multiline = True
+ self.line_number = token[2][0]
+ for line in token[1].split('\n')[:-1]:
+ self.check_physical(line + '\n')
+ self.line_number += 1
+ self.multiline = False
+
def check_all(self, expected=None, line_offset=0):
- """
- Run all checks on the input file.
- """
+ """Run all checks on the input file."""
self.report.init_file(self.filename, self.lines, expected, line_offset)
+ self.total_lines = len(self.lines)
if self._ast_checks:
self.check_ast()
self.line_number = 0
self.indent_char = None
- self.indent_level = 0
+ self.indent_level = self.previous_indent_level = 0
self.previous_logical = ''
self.tokens = []
- self.blank_lines = blank_lines_before_comment = 0
+ self.blank_lines = self.blank_before = 0
parens = 0
for token in self.generate_tokens():
self.tokens.append(token)
@@ -1372,29 +1433,33 @@
elif text in '}])':
parens -= 1
elif not parens:
- if token_type == tokenize.NEWLINE:
- if self.blank_lines < blank_lines_before_comment:
- self.blank_lines = blank_lines_before_comment
- self.check_logical()
- self.tokens = []
- self.blank_lines = blank_lines_before_comment = 0
- elif token_type == tokenize.NL:
- if len(self.tokens) == 1:
+ if token_type in NEWLINE:
+ if token_type == tokenize.NEWLINE:
+ self.check_logical()
+ self.blank_before = 0
+ elif len(self.tokens) == 1:
# The physical line contains only this token.
self.blank_lines += 1
- self.tokens = []
- elif token_type == tokenize.COMMENT and len(self.tokens) == 1:
- if blank_lines_before_comment < self.blank_lines:
- blank_lines_before_comment = self.blank_lines
- self.blank_lines = 0
- if COMMENT_WITH_NL:
+ del self.tokens[0]
+ else:
+ self.check_logical()
+ elif COMMENT_WITH_NL and token_type == tokenize.COMMENT:
+ if len(self.tokens) == 1:
# The comment also ends a physical line
- self.tokens = []
+ token = list(token)
+ token[1] = text.rstrip('\r\n')
+ token[3] = (token[2][0], token[2][1] + len(token[1]))
+ self.tokens = [tuple(token)]
+ self.check_logical()
+ if self.tokens:
+ self.check_physical(self.lines[-1])
+ self.check_logical()
return self.report.get_file_results()
class BaseReport(object):
"""Collect the results of the checks."""
+
print_filename = False
def __init__(self, options):
@@ -1457,8 +1522,7 @@
for key in self.messages if key.startswith(prefix)])
def get_statistics(self, prefix=''):
- """
- Get statistics for message codes that start with the prefix.
+ """Get statistics for message codes that start with the prefix.
prefix='' matches all errors and warnings
prefix='E' matches all errors
@@ -1529,9 +1593,9 @@
else:
line = self.lines[line_number - 1]
print(line.rstrip())
- print(' ' * offset + '^')
+ print(re.sub(r'\S', ' ', line[:offset]) + '^')
if self._show_pep8 and doc:
- print(doc.lstrip('\n').rstrip())
+ print(' ' + doc.strip())
return self.file_errors
@@ -1557,11 +1621,12 @@
parse_argv = kwargs.pop('parse_argv', False)
config_file = kwargs.pop('config_file', None)
parser = kwargs.pop('parser', None)
+ # build options from dict
+ options_dict = dict(*args, **kwargs)
+ arglist = None if parse_argv else options_dict.get('paths', None)
options, self.paths = process_options(
- parse_argv=parse_argv, config_file=config_file, parser=parser)
- if args or kwargs:
- # build options from dict
- options_dict = dict(*args, **kwargs)
+ arglist, parse_argv, config_file, parser)
+ if options_dict:
options.__dict__.update(options_dict)
if 'paths' in options_dict:
self.paths = options_dict['paths']
@@ -1572,11 +1637,14 @@
if not options.reporter:
options.reporter = BaseReport if options.quiet else StandardReport
- for index, value in enumerate(options.exclude):
- options.exclude[index] = value.rstrip('/')
- # Ignore all checks which are not explicitly selected
options.select = tuple(options.select or ())
- options.ignore = tuple(options.ignore or options.select and ('',))
+ if not (options.select or options.ignore or
+ options.testsuite or options.doctest) and DEFAULT_IGNORE:
+ # The default choice: ignore controversial checks
+ options.ignore = tuple(DEFAULT_IGNORE.split(','))
+ else:
+ # Ignore all checks which are not explicitly selected
+ options.ignore = ('',) if options.select else tuple(options.ignore)
options.benchmark_keys = BENCHMARK_KEYS[:]
options.ignore_code = self.ignore_code
options.physical_checks = self.get_checks('physical_line')
@@ -1629,37 +1697,45 @@
print('directory ' + root)
counters['directories'] += 1
for subdir in sorted(dirs):
- if self.excluded(os.path.join(root, subdir)):
+ if self.excluded(subdir, root):
dirs.remove(subdir)
for filename in sorted(files):
# contain a pattern that matches?
if ((filename_match(filename, filepatterns) and
- not self.excluded(filename))):
+ not self.excluded(filename, root))):
runner(os.path.join(root, filename))
- def excluded(self, filename):
+ def excluded(self, filename, parent=None):
+ """Check if the file should be excluded.
+
+ Check if 'options.exclude' contains a pattern that matches filename.
"""
- Check if options.exclude contains a pattern that matches filename.
- """
+ if not self.options.exclude:
+ return False
basename = os.path.basename(filename)
- return any((filename_match(filename, self.options.exclude,
- default=False),
- filename_match(basename, self.options.exclude,
- default=False)))
+ if filename_match(basename, self.options.exclude):
+ return True
+ if parent:
+ filename = os.path.join(parent, filename)
+ filename = os.path.abspath(filename)
+ return filename_match(filename, self.options.exclude)
def ignore_code(self, code):
- """
- Check if the error code should be ignored.
+ """Check if the error code should be ignored.
If 'options.select' contains a prefix of the error code,
return False. Else, if 'options.ignore' contains a prefix of
the error code, return True.
"""
+ if len(code) < 4 and any(s.startswith(code)
+ for s in self.options.select):
+ return False
return (code.startswith(self.options.ignore) and
not code.startswith(self.options.select))
def get_checks(self, argument_name):
- """
+ """Get all the checks for this category.
+
Find all globally visible functions where the first argument name
starts with argument_name and which contain selected tests.
"""
@@ -1675,8 +1751,9 @@
parser = OptionParser(prog=prog, version=version,
usage="%prog [options] input ...")
parser.config_options = [
- 'exclude', 'filename', 'select', 'ignore', 'max-line-length', 'count',
- 'format', 'quiet', 'show-pep8', 'show-source', 'statistics', 'verbose']
+ 'exclude', 'filename', 'select', 'ignore', 'max-line-length',
+ 'hang-closing', 'count', 'format', 'quiet', 'show-pep8',
+ 'show-source', 'statistics', 'verbose']
parser.add_option('-v', '--verbose', default=0, action='count',
help="print status messages, or debug with -vv")
parser.add_option('-q', '--quiet', default=0, action='count',
@@ -1711,6 +1788,9 @@
default=MAX_LINE_LENGTH,
help="set maximum allowed line length "
"(default: %default)")
+ parser.add_option('--hang-closing', action='store_true',
+ help="hang closing bracket instead of matching "
+ "indentation of opening bracket's line")
parser.add_option('--format', metavar='format', default='default',
help="set the error format [default|pylint|<custom>]")
parser.add_option('--diff', action='store_true',
@@ -1737,19 +1817,15 @@
print('user configuration: %s' % user_conf)
config.read(user_conf)
+ local_dir = os.curdir
parent = tail = args and os.path.abspath(os.path.commonprefix(args))
while tail:
- for name in PROJECT_CONFIG:
- local_conf = os.path.join(parent, name)
- if os.path.isfile(local_conf):
- break
- else:
- parent, tail = os.path.split(parent)
- continue
- if options.verbose:
- print('local configuration: %s' % local_conf)
- config.read(local_conf)
- break
+ if config.read([os.path.join(parent, fn) for fn in PROJECT_CONFIG]):
+ local_dir = parent
+ if options.verbose:
+ print('local configuration: in %s' % parent)
+ break
+ (parent, tail) = os.path.split(parent)
pep8_section = parser.prog
if config.has_section(pep8_section):
@@ -1757,29 +1833,30 @@
for o in parser.option_list])
# First, read the default values
- new_options, _ = parser.parse_args([])
+ (new_options, __) = parser.parse_args([])
# Second, parse the configuration
for opt in config.options(pep8_section):
+ if opt.replace('_', '-') not in parser.config_options:
+ print(" unknown option '%s' ignored" % opt)
+ continue
if options.verbose > 1:
print(" %s = %s" % (opt, config.get(pep8_section, opt)))
- if opt.replace('_', '-') not in parser.config_options:
- print("Unknown option: '%s'\n not in [%s]" %
- (opt, ' '.join(parser.config_options)))
- sys.exit(1)
normalized_opt = opt.replace('-', '_')
opt_type = option_list[normalized_opt]
if opt_type in ('int', 'count'):
value = config.getint(pep8_section, opt)
elif opt_type == 'string':
value = config.get(pep8_section, opt)
+ if normalized_opt == 'exclude':
+ value = normalize_paths(value, local_dir)
else:
assert opt_type in ('store_true', 'store_false')
value = config.getboolean(pep8_section, opt)
setattr(new_options, normalized_opt, value)
# Third, overwrite with the command-line options
- options, _ = parser.parse_args(arglist, values=new_options)
+ (options, __) = parser.parse_args(arglist, values=new_options)
options.doctest = options.testsuite = False
return options
@@ -1787,9 +1864,6 @@
def process_options(arglist=None, parse_argv=False, config_file=None,
parser=None):
"""Process options passed either via arglist or via command line args."""
- if not arglist and not parse_argv:
- # Don't read the command line if the module is used as a library.
- arglist = []
if not parser:
parser = get_parser()
if not parser.has_option('--config'):
@@ -1802,7 +1876,12 @@
(parser.prog, ', '.join(parser.config_options))))
group.add_option('--config', metavar='path', default=config_file,
help="user config file location (default: %default)")
- options, args = parser.parse_args(arglist)
+ # Don't read the command line if the module is used as a library.
+ if not arglist and not parse_argv:
+ arglist = []
+ # If parse_argv is True and arglist is None, arguments are
+ # parsed from the command line (sys.argv)
+ (options, args) = parser.parse_args(arglist)
options.reporter = None
if options.ensure_value('testsuite', False):
@@ -1817,18 +1896,10 @@
options = read_config(options, args, arglist, parser)
options.reporter = parse_argv and options.quiet == 1 and FileReport
- if options.filename:
- options.filename = options.filename.split(',')
- options.exclude = options.exclude.split(',')
- if options.select:
- options.select = options.select.split(',')
- if options.ignore:
- options.ignore = options.ignore.split(',')
- elif not (options.select or
- options.testsuite or options.doctest) and DEFAULT_IGNORE:
- # The default choice: ignore controversial checks
- # (for doctest and testsuite, all checks are required)
- options.ignore = DEFAULT_IGNORE.split(',')
+ options.filename = options.filename and options.filename.split(',')
+ options.exclude = normalize_paths(options.exclude)
+ options.select = options.select and options.select.split(',')
+ options.ignore = options.ignore and options.ignore.split(',')
if options.diff:
options.reporter = DiffReport
@@ -1841,13 +1912,19 @@
def _main():
"""Parse options and run checks on Python source."""
+ import signal
+
+ # Handle "Broken pipe" gracefully
+ try:
+ signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1))
+ except AttributeError:
+ pass # not supported on Windows
+
pep8style = StyleGuide(parse_argv=True, config_file=True)
options = pep8style.options
if options.doctest or options.testsuite:
- sys.path[:0] = [TESTSUITE_PATH]
- from test_pep8 import run_tests
- del sys.path[0]
- report = run_tests(pep8style, options.doctest, options.testsuite)
+ from testsuite.support import run_tests
+ report = run_tests(pep8style)
else:
report = pep8style.check_files()
if options.statistics:
diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py
new file mode 100644
index 0000000..6aaa566
--- /dev/null
+++ b/python/helpers/pycharm/lettuce_runner.py
@@ -0,0 +1,132 @@
+# coding=utf-8
+"""
+BDD lettuce framework runner
+"""
+__author__ = 'Ilya.Kazakevich'
+import os
+from lettuce.exceptions import ReasonToFail
+import time
+import sys
+import tcmessages
+import lettuce
+from lettuce import core
+
+
+# Error message about unsupported outlines
+_NO_OUTLINE_ERROR = "Outline scenarios are not supported due to https://github.com/gabrielfalcao/lettuce/issues/451"
+
+
+class LettuceRunner(object):
+ """
+ TODO: Runs lettuce
+ """
+
+ def __init__(self, base_dir):
+ """
+ :param base_dir base directory to run tests in
+ :type base_dir: str
+
+ """
+ self.base_dir = base_dir
+ self.runner = lettuce.Runner(base_dir)
+ self.messages = tcmessages.TeamcityServiceMessages()
+ self.test_start_time = None
+
+ def report_tests(self):
+ """
+ :returns : number of tests
+ :rtype : int
+ """
+ result = 0
+ for feature_file in self.runner.loader.find_feature_files():
+ feature = core.Feature.from_file(feature_file)
+ for scenario in feature.scenarios:
+ assert isinstance(scenario, core.Scenario), scenario
+ if not scenario.outlines:
+ result += len(scenario.steps)
+ self.messages.testCount(result)
+
+ def report_scenario_started(self, scenario):
+ """
+ Reports scenario launched
+ :type scenario core.Scenario
+ :param scenario: scenario
+ """
+ if scenario.outlines:
+ self.messages.testIgnored(scenario.name,
+ _NO_OUTLINE_ERROR)
+ scenario.steps = [] # Clear to prevent running. TODO: Fix when this issue fixed
+ scenario.background = None # TODO: undocumented
+ return
+ self.report_suite(True, scenario.name, scenario.described_at)
+
+ def report_suite(self, is_start, name, described_at):
+ """
+ Reports some suite (scenario, feature, background etc) is started or stopped
+ :param is_start: started or not
+ :param name: suite name
+ :param described_at: where it is described (file, line)
+ :return:
+ """
+ if is_start:
+ self.messages.testSuiteStarted(name, self._gen_location(described_at))
+ else:
+ self.messages.testSuiteFinished(name)
+
+ def report_step(self, is_start, step):
+ """
+ Reports step start / stop
+ :param is_start: true if step started
+ :type step core.Step
+ :param step: step
+ """
+ test_name = step.sentence
+ if is_start:
+ self.test_start_time = time.time()
+ self.messages.testStarted(test_name, self._gen_location(step.described_at))
+ elif step.passed:
+ duration = 0
+ if self.test_start_time:
+ duration = long(time.time() - self.test_start_time)
+ self.messages.testFinished(test_name, duration=duration)
+ self.test_start_time = None
+ elif step.failed:
+ reason = step.why
+ assert isinstance(reason, ReasonToFail), reason
+ self.messages.testFailed(test_name, message=reason.exception, details=reason.traceback)
+
+ def _gen_location(self, description):
+ """
+ :param description: "described_at" (file, line)
+ :return: location in format file:line by "described_at"
+ """
+ return "file:///{}/{}:{}".format(self.base_dir, description.file, description.line)
+
+ def run(self):
+ """
+ Launches runner
+ """
+ self.report_tests()
+ self.messages.testMatrixEntered()
+
+ lettuce.before.each_feature(lambda f: self.report_suite(True, f.name, f.described_at))
+ lettuce.after.each_feature(lambda f: self.report_suite(False, f.name, f.described_at))
+
+ lettuce.before.each_scenario(lambda s: self.report_scenario_started(s))
+ lettuce.after.each_scenario(lambda s: self.report_suite(False, s.name, s.described_at))
+
+ lettuce.before.each_background(
+ lambda b, *args: self.report_suite(True, "Scenario background", b.feature.described_at))
+ lettuce.after.each_background(
+ lambda b, *args: self.report_suite(False, "Scenario background", b.feature.described_at))
+
+ lettuce.before.each_step(lambda s: self.report_step(True, s))
+ lettuce.after.each_step(lambda s: self.report_step(False, s))
+
+ self.runner.run()
+
+
+if __name__ == "__main__":
+ path = sys.argv[1] if len(sys.argv) > 1 else "."
+ assert os.path.exists(path), "{} does not exist".format(path)
+ LettuceRunner(path).run()
\ No newline at end of file
diff --git a/python/helpers/pycharm_generator_utils/module_redeclarator.py b/python/helpers/pycharm_generator_utils/module_redeclarator.py
index 3af1961..0dbdbb5 100644
--- a/python/helpers/pycharm_generator_utils/module_redeclarator.py
+++ b/python/helpers/pycharm_generator_utils/module_redeclarator.py
@@ -1007,6 +1007,8 @@
if self.doing_builtins and p_name == BUILTIN_MOD_NAME:
txt = create_generator()
self.classes_buf.out(0, txt)
+ txt = create_function()
+ self.classes_buf.out(0, txt)
# Fake <type 'namedtuple'>
if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
diff --git a/python/helpers/pycharm_generator_utils/util_methods.py b/python/helpers/pycharm_generator_utils/util_methods.py
index d679e33..b6805c4 100644
--- a/python/helpers/pycharm_generator_utils/util_methods.py
+++ b/python/helpers/pycharm_generator_utils/util_methods.py
@@ -75,6 +75,47 @@
"""
return txt
+def create_function():
+ txt = """
+class __function(object):
+ '''A mock class representing function type.'''
+
+ def __init__(self):
+ self.__name__ = ''
+ self.__doc__ = ''
+ self.__dict__ = ''
+ self.__module__ = ''
+"""
+ if version[0] == 2:
+ txt += """
+ self.func_defaults = {}
+ self.func_globals = {}
+ self.func_closure = None
+ self.func_code = None
+ self.func_name = ''
+ self.func_doc = ''
+ self.func_dict = ''
+"""
+ if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
+ txt += """
+ self.__defaults__ = {}
+ self.__globals__ = {}
+ self.__closure__ = None
+ self.__code__ = None
+ self.__name__ = ''
+"""
+ if version[0] >= 3:
+ txt += """
+ self.__annotations__ = {}
+ self.__kwdefaults__ = {}
+"""
+ if version[0] >= 3 and version[1] >= 3:
+ txt += """
+ self.__qualname__ = ''
+"""
+ return txt
+
+
def _searchbases(cls, accum):
# logic copied from inspect.py
if cls not in accum:
diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py
index 9d4f2e7..b4cf585 100644
--- a/python/helpers/pydev/pydevd_comm.py
+++ b/python/helpers/pydev/pydevd_comm.py
@@ -427,7 +427,7 @@
s = socket(AF_INET, SOCK_STREAM)
- MAX_TRIES = 20
+ MAX_TRIES = 100
i = 0
while i<MAX_TRIES:
try:
diff --git a/python/helpers/pydev/pydevd_signature.py b/python/helpers/pydev/pydevd_signature.py
index 2f9a182..e11bb5d 100644
--- a/python/helpers/pydev/pydevd_signature.py
+++ b/python/helpers/pydev/pydevd_signature.py
@@ -47,7 +47,7 @@
name = code.co_varnames[i]
tp = type(locals[name])
class_name = tp.__name__
- if class_name == 'instance':
+ if class_name == 'instance': # old-style classes
tp = locals[name].__class__
class_name = tp.__name__
diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java
index 8e2430e..94e35f7 100644
--- a/python/psi-api/src/com/jetbrains/python/PyNames.java
+++ b/python/psi-api/src/com/jetbrains/python/PyNames.java
@@ -59,6 +59,7 @@
public static final String FAKE_OLD_BASE = "___Classobj";
public static final String FAKE_GENERATOR = "__generator";
+ public static final String FAKE_FUNCTION = "__function";
public static final String FAKE_NAMEDTUPLE = "__namedtuple";
public static final String FUTURE_MODULE = "__future__";
@@ -167,17 +168,24 @@
*/
public static ImmutableSet<String> UnderscoredAttributes = ImmutableSet.of(
"__all__",
+ "__annotations__",
"__author__",
"__bases__",
+ "__closure__",
+ "__code__",
"__defaults__",
"__dict__",
+ "__dir__",
"__doc__",
"__docformat__",
"__file__",
+ "__func__",
+ "__globals__",
"__kwdefaults__",
"__members__",
"__metaclass__",
"__mod__",
+ "__module__",
"__mro__",
"__name__",
"__path__",
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
index a307c03..9e347e0 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
@@ -10,7 +10,9 @@
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.TimeoutUtil;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.io.BaseOutputReader;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import com.jetbrains.python.debugger.*;
@@ -25,6 +27,7 @@
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
public class RemoteDebugger implements ProcessDebugger {
@@ -87,8 +90,7 @@
if (myConnected) {
try {
- myDebuggerReader = new DebuggerReader();
- ApplicationManager.getApplication().executeOnPooledThread(myDebuggerReader);
+ myDebuggerReader = createReader(mySocket);
}
catch (Exception e) {
synchronized (mySocketObject) {
@@ -405,35 +407,42 @@
execute(command);
}
- private class DebuggerReader implements Runnable {
- private final InputStream myInputStream;
- private boolean myClosing = false;
+ public DebuggerReader createReader(@NotNull Socket socket) throws IOException {
+ synchronized (mySocketObject) {
+ final InputStream myInputStream = socket.getInputStream();
+ //noinspection IOResourceOpenedButNotSafelyClosed
+ final Reader reader = new InputStreamReader(myInputStream, Charset.forName("UTF-8")); //TODO: correct econding?
+ return new DebuggerReader(reader);
+ }
+ }
- private DebuggerReader() throws IOException {
- synchronized (mySocketObject) {
- this.myInputStream = mySocket.getInputStream();
- }
+ private class DebuggerReader extends BaseOutputReader {
+ private boolean myClosing = false;
+ private Reader myReader;
+
+ private DebuggerReader(final Reader reader) throws IOException {
+ super(reader);
+ myReader = reader;
+ start();
}
- public void run() {
- final BufferedReader reader = new BufferedReader(new InputStreamReader(myInputStream, Charset.forName("UTF-8")));
+ protected void doRun() {
try {
- String line;
- while ((line = reader.readLine()) != null) {
- processResponse(line);
+ while (true) {
+ boolean read = readAvailable();
+
if (myClosing) {
break;
}
+
+ TimeoutUtil.sleep(mySleepingPolicy.getTimeToSleep(read));
}
}
- catch (SocketException ignore) {
+ catch (Exception e) {
fireCommunicationError();
}
- catch (Exception e) {
- LOG.error(e);
- }
finally {
- closeReader(reader);
+ closeReader(myReader);
fireExitEvent();
}
}
@@ -518,7 +527,7 @@
return ProtocolParser.parseThread(frame.getPayload(), myDebugProcess.getPositionConverter());
}
- private void closeReader(BufferedReader reader) {
+ private void closeReader(Reader reader) {
try {
reader.close();
}
@@ -526,9 +535,19 @@
}
}
+ @Override
+ protected Future<?> executeOnPooledThread(Runnable runnable) {
+ return ApplicationManager.getApplication().executeOnPooledThread(runnable);
+ }
+
public void close() {
myClosing = true;
}
+
+ @Override
+ protected void onTextAvailable(@NotNull String text) {
+ processResponse(text);
+ }
}
private void writeToConsole(PyIo io) {
diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml
index 775d912..13ba05d 100644
--- a/python/src/META-INF/pycharm-core.xml
+++ b/python/src/META-INF/pycharm-core.xml
@@ -33,7 +33,7 @@
<projectConfigurable instance="com.jetbrains.python.configuration.PythonContentEntriesConfigurable"/>
<projectConfigurable instance="com.jetbrains.python.buildout.BuildoutModulesConfigurable"/>
- <projectConfigurable instance="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"/>
+ <projectConfigurable groupId="project" instance="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"/>
<projectConfigurable instance="com.jetbrains.python.configuration.PyDependenciesConfigurable"/>
<directoryProjectConfigurator implementation="com.jetbrains.python.PythonSdkConfigurator" id="sdk"
diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml
index 54491eb..88280cc 100644
--- a/python/src/META-INF/python-core.xml
+++ b/python/src/META-INF/python-core.xml
@@ -387,8 +387,6 @@
<completion.contributor language="Python"
implementationClass="com.jetbrains.python.codeInsight.completion.PyClassNameCompletionContributor"/>
- <completion.contributor language="Python"
- implementationClass="com.jetbrains.python.codeInsight.completion.PyBracketProtectingCompletionContributor"/>
<weigher key="completion" implementationClass="com.jetbrains.python.codeInsight.completion.PythonCompletionWeigher" order="first"/>
<completion.confidence language="Python" implementationClass="com.jetbrains.python.codeInsight.completion.PyCompletionConfidence"/>
<typedHandler implementation="com.jetbrains.python.console.completion.PythonConsoleAutopopupBlockingHandler" id="pydevBlockAutoPopup"
@@ -446,10 +444,10 @@
<applicationService serviceInterface="com.jetbrains.python.documentation.PythonDocumentationMap"
serviceImplementation="com.jetbrains.python.documentation.PythonDocumentationMap"/>
- <applicationConfigurable instance="com.jetbrains.python.documentation.PythonDocumentationConfigurable"
+ <applicationConfigurable groupId="tools" instance="com.jetbrains.python.documentation.PythonDocumentationConfigurable"
id="com.jetbrains.python.documentation.PythonDocumentationConfigurable"
displayName="Python External Documentation"/>
- <projectConfigurable instance="com.jetbrains.python.configuration.PyIntegratedToolsModulesConfigurable"
+ <projectConfigurable groupId="tools" instance="com.jetbrains.python.configuration.PyIntegratedToolsModulesConfigurable"
id="com.jetbrains.python.configuration.PyIntegratedToolsModulesConfigurable" displayName="Python Integrated Tools"
nonDefaultProject="true"/>
<moduleService serviceInterface="com.jetbrains.python.documentation.PyDocumentationSettings"
@@ -465,10 +463,10 @@
<project.converterProvider implementation="com.jetbrains.python.testing.converters.PythonTestConverterProvider"/>
- <projectConfigurable instance="com.jetbrains.python.console.PyConsoleOptionsConfigurable"/>
+ <projectConfigurable groupId="build" dynamic="true" instance="com.jetbrains.python.console.PyConsoleOptionsConfigurable"/>
<projectService serviceImplementation="com.jetbrains.python.console.PyConsoleOptions"/>
- <projectConfigurable instance="com.jetbrains.python.debugger.PyDebuggerConfigurable"/>
+ <projectConfigurable groupId="build" instance="com.jetbrains.python.debugger.PyDebuggerConfigurable"/>
<projectService serviceImplementation="com.jetbrains.python.debugger.PyDebuggerOptionsProvider"/>
<codeBlockProvider language="Python" implementationClass="com.jetbrains.python.codeInsight.PyCodeBlockProvider"/>
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java
deleted file mode 100644
index 766e5be..0000000
--- a/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.codeInsight.completion;
-
-import com.intellij.codeInsight.completion.CompletionContributor;
-import com.intellij.codeInsight.completion.CompletionInitializationContext;
-import com.intellij.psi.PsiReference;
-import com.jetbrains.python.psi.impl.references.PyOperatorReference;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author yole
- */
-public class PyBracketProtectingCompletionContributor extends CompletionContributor {
- @Override
- public void beforeCompletion(@NotNull CompletionInitializationContext context) {
- PsiReference ref = context.getFile().findReferenceAt(context.getSelectionEndOffset());
- if (ref instanceof PyOperatorReference) {
- context.setReplacementOffset(context.getIdentifierEndOffset());
- }
- }
-}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
index 725cd2d..e27f2a6 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.codeInsight.editorActions.smartEnter;
+import com.google.common.collect.ImmutableList;
import com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -48,25 +49,22 @@
*/
public class PySmartEnterProcessor extends SmartEnterProcessor {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor");
- private static final List<PyFixer> ourFixers = new ArrayList<PyFixer>();
- private static final List<EnterProcessor> ourProcessors = new ArrayList<EnterProcessor>();
-
- static {
- ourFixers.add(new PyStringLiteralFixer());
- ourFixers.add(new PyParenthesizedFixer());
- ourFixers.add(new PyMissingBracesFixer());
- ourFixers.add(new PyConditionalStatementPartFixer());
- ourFixers.add(new PyUnconditionalStatementPartFixer());
- ourFixers.add(new PyForPartFixer());
- ourFixers.add(new PyExceptFixer());
- ourFixers.add(new PyArgumentListFixer());
- ourFixers.add(new PyParameterListFixer());
- ourFixers.add(new PyFunctionFixer());
- ourFixers.add(new PyClassFixer());
-
- ourProcessors.add(new PyCommentBreakerEnterProcessor());
- ourProcessors.add(new PyPlainEnterProcessor());
- }
+ private static final List<PyFixer> ourFixers = ImmutableList.<PyFixer>builder()
+ .add(new PyStringLiteralFixer())
+ .add(new PyParenthesizedFixer())
+ .add(new PyMissingBracesFixer())
+ .add(new PyConditionalStatementPartFixer())
+ .add(new PyUnconditionalStatementPartFixer())
+ .add(new PyForPartFixer())
+ .add(new PyExceptFixer())
+ .add(new PyArgumentListFixer())
+ .add(new PyParameterListFixer())
+ .add(new PyFunctionFixer())
+ .add(new PyClassFixer())
+ .add(new PyWithFixer())
+ .build();
+ private static final List<EnterProcessor> ourProcessors = ImmutableList.of(new PyCommentBreakerEnterProcessor(),
+ new PyPlainEnterProcessor());
private static class TooManyAttemptsException extends Exception {
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
index d671790..ac2e312 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
@@ -20,10 +20,7 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.SmartEnterUtil;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.PyStatementList;
-import com.jetbrains.python.psi.PyStatementPart;
+import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.Nullable;
/**
@@ -43,10 +40,14 @@
}
else if (psiElement instanceof PyClass) {
return ((PyClass)psiElement).getStatementList();
- } else {
- final CaretModel caretModel = editor.getCaretModel();
- final PsiElement atCaret = psiElement.getContainingFile().findElementAt(caretModel.getOffset());
- PyStatementPart statementPart = PsiTreeUtil.getParentOfType(atCaret, PyStatementPart.class);
+ }
+ else if (psiElement instanceof PyWithStatement) {
+ return PsiTreeUtil.getChildOfType(psiElement, PyStatementList.class);
+ }
+ else {
+ final CaretModel caretModel = editor.getCaretModel();
+ final PsiElement atCaret = psiElement.getContainingFile().findElementAt(caretModel.getOffset());
+ final PyStatementPart statementPart = PsiTreeUtil.getParentOfType(atCaret, PyStatementPart.class);
if (statementPart != null) {
return statementPart.getStatementList();
}
@@ -55,7 +56,7 @@
}
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
- PyStatementList statementList = getStatementList(psiElement, editor);
+ final PyStatementList statementList = getStatementList(psiElement, editor);
if (statementList != null && statementList.getStatements().length == 0) {
SmartEnterUtil.plainEnter(editor);
//editor.getCaretModel().moveToOffset(statementList.getTextRange().getEndOffset());
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
new file mode 100644
index 0000000..b6d099e
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.python.PyTokenTypes;
+import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
+import com.jetbrains.python.psi.PyElementType;
+import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.PyWithItem;
+import com.jetbrains.python.psi.PyWithStatement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyWithFixer implements PyFixer {
+ public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
+ if (psiElement instanceof PyWithStatement) {
+ final PyWithStatement withStatement = (PyWithStatement)psiElement;
+ final PsiElement colonToken = getFirstChildOfType(psiElement, PyTokenTypes.COLON);
+ final PsiElement withToken = getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD);
+ final Document document = editor.getDocument();
+ if (colonToken == null) {
+ int insertAt = sure(withToken).getTextRange().getEndOffset();
+ String textToInsert = ":";
+ final PyWithItem[] withItems = withStatement.getWithItems();
+ final PyWithItem lastItem = withItems.length != 0 ? withItems[withItems.length - 1] : null;
+ if (lastItem == null || lastItem.getExpression() == null) {
+ textToInsert = " :";
+ processor.registerUnresolvedError(insertAt + 1);
+ }
+ else {
+ final PyExpression expression = lastItem.getExpression();
+ insertAt = expression.getTextRange().getEndOffset();
+ final PsiElement asToken = getFirstChildOfType(lastItem, PyTokenTypes.AS_KEYWORD);
+ if (asToken != null) {
+ insertAt = asToken.getTextRange().getEndOffset();
+ final PyExpression target = lastItem.getTarget();
+ if (target != null) {
+ insertAt = target.getTextRange().getEndOffset();
+ }
+ else {
+ textToInsert = " :";
+ processor.registerUnresolvedError(insertAt + 1);
+ }
+ }
+ }
+ document.insertString(insertAt, textToInsert);
+ }
+ }
+ }
+
+ @Nullable
+ private static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
+ final ASTNode child = element.getNode().findChildByType(type);
+ return child != null ? child.getPsi() : null;
+ }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertFormatOperatorToMethodIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertFormatOperatorToMethodIntention.java
index 3df0a3e..0ba35ed 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertFormatOperatorToMethodIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertFormatOperatorToMethodIntention.java
@@ -290,7 +290,10 @@
}
}
else target.append("(").append(paramText).append(")"); // tuple is ok as is
- element.replace(elementGenerator.createExpressionFromText(LanguageLevel.forElement(element), target.toString()));
+ // Correctly handle multiline implicitly concatenated string literals (PY-9176)
+ target.insert(0, '(').append(')');
+ final PyExpression parenthesized = elementGenerator.createExpressionFromText(LanguageLevel.forElement(element), target.toString());
+ element.replace(sure(((PyParenthesizedExpression)parenthesized).getContainedExpression()));
}
private static String getSeparator(PyStringLiteralExpression leftExpression) {
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
index ba2f137..3d77ac4 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,8 +52,8 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
@@ -360,7 +360,7 @@
manager.createRemoteProcess(getProject(), myRemoteCredentials, mappings, commandLine, true);
- Pair<Integer, Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
+ Couple<Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
remoteProcess.addLocalTunnel(myPorts[0], myRemoteCredentials.getHost(), remotePorts.first);
remoteProcess.addRemoteTunnel(remotePorts.second, "localhost", myPorts[1]);
@@ -374,10 +374,10 @@
}
}
- private static Pair<Integer, Integer> getRemotePortsFromProcess(RemoteSshProcess process) throws ExecutionException {
+ private static Couple<Integer> getRemotePortsFromProcess(RemoteSshProcess process) throws ExecutionException {
Scanner s = new Scanner(process.getInputStream());
- return Pair.create(readInt(s, process), readInt(s, process));
+ return Couple.of(readInt(s, process), readInt(s, process));
}
private static int readInt(Scanner s, Process process) throws ExecutionException {
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
index 3e13332..7d087bd 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
@@ -19,12 +19,15 @@
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.execution.process.ProcessOutput;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.progress.Task.Backgroundable;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
@@ -65,6 +68,7 @@
/**
* Generates pack of fixes available for some unresolved import statement.
* Be sure to call {@link #isApplicable(com.jetbrains.python.psi.PyImportStatementBase)} first to make sure this statement is supported
+ *
* @param importStatementBase statement to fix
* @return pack of fixes
*/
@@ -73,14 +77,14 @@
final List<String> names = importStatementBase.getFullyQualifiedObjectNames();
final List<GenerateBinaryStubsFix> result = new ArrayList<GenerateBinaryStubsFix>(names.size());
for (final String qualifiedName : names) {
- result.add(new GenerateBinaryStubsFix(importStatementBase, qualifiedName));
+ result.add(new GenerateBinaryStubsFix(importStatementBase, qualifiedName));
}
return result;
}
/**
* @param importStatementBase statement to fix
- * @param qualifiedName name should be fixed (one of {@link com.jetbrains.python.psi.PyImportStatementBase#getFullyQualifiedObjectNames()})
+ * @param qualifiedName name should be fixed (one of {@link com.jetbrains.python.psi.PyImportStatementBase#getFullyQualifiedObjectNames()})
*/
private GenerateBinaryStubsFix(@NotNull final PyImportStatementBase importStatementBase, @NotNull final String qualifiedName) {
myQualifiedName = qualifiedName;
@@ -102,15 +106,34 @@
@Override
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
final PsiFile file = descriptor.getPsiElement().getContainingFile();
- final String folder = file.getContainingDirectory().getVirtualFile().getCanonicalPath();
+ final Backgroundable backgroundable = getFixTask(file);
+ ProgressManager.getInstance().runProcessWithProgressAsynchronously(backgroundable, new BackgroundableProcessIndicator(backgroundable));
+ }
- final Task.Backgroundable backgroundable = new Task.Backgroundable(project, "Generating skeletons for binary module", false) {
+
+ /**
+ * Returns fix task that is used to generate stubs
+ * @param fileToRunTaskIn file where task should run
+ * @return task itself
+ */
+ @NotNull
+ public Backgroundable getFixTask(@NotNull final PsiFile fileToRunTaskIn) {
+ final Project project = fileToRunTaskIn.getProject();
+ final String folder = fileToRunTaskIn.getContainingDirectory().getVirtualFile().getCanonicalPath();
+ return new Task.Backgroundable(project, "Generating skeletons for binary module", false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
- final List<String> assemblyRefs = collectAssemblyReferences(file);
+
+ final List<String> assemblyRefs = new ReadAction<List<String>>() {
+ @Override
+ protected void run(@NotNull Result<List<String>> result) throws Throwable {
+ result.setResult(collectAssemblyReferences(fileToRunTaskIn));
+ }
+ }.execute().getResultObject();
+
try {
final PySkeletonRefresher refresher = new PySkeletonRefresher(project, null, mySdk, null, null, folder);
@@ -133,7 +156,6 @@
}
}
};
- ProgressManager.getInstance().runProcessWithProgressAsynchronously(backgroundable, new BackgroundableProcessIndicator(backgroundable));
}
private boolean generateSkeletonsForList(@NotNull final PySkeletonRefresher refresher,
@@ -185,8 +207,8 @@
// TODO: What if user loads it not by literal? We need to ask user for list of DLLs
if (node.isCalleeText("AddReference", "AddReferenceByPartialName", "AddReferenceByName")) {
final PyExpression[] args = node.getArguments();
- if (args.length == 1 && args [0] instanceof PyStringLiteralExpression) {
- result.add(((PyStringLiteralExpression) args [0]).getStringValue());
+ if (args.length == 1 && args[0] instanceof PyStringLiteralExpression) {
+ result.add(((PyStringLiteralExpression)args[0]).getStringValue());
}
}
}
diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
index fac391a..c0fb3c1 100644
--- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
@@ -464,7 +464,9 @@
}
if (expr.isQualified()) {
final PyClassTypeImpl object_type = (PyClassTypeImpl)PyBuiltinCache.getInstance(node).getObjectType();
- if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)) return;
+ if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)){
+ return;
+ }
}
else {
if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(expr)) {
@@ -494,7 +496,9 @@
}
// unqualified:
// may be module's
- if (PyModuleType.getPossibleInstanceMembers().contains(refName)) return;
+ if (PyModuleType.getPossibleInstanceMembers().contains(refName)) {
+ return;
+ }
// may be a "try: import ..."; not an error not to resolve
if ((
PsiTreeUtil.getParentOfType(
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
index 359e497..65d95eb 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
@@ -795,14 +795,22 @@
private String getHelperPath(String helper) {
String helperPath;
final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
- if (sdkData instanceof RemoteSdkCredentials) {
- final RemoteSdkCredentials remoteSdkCredentials = (RemoteSdkCredentials)sdkData;
- if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
- helperPath = new RemoteFile(remoteSdkCredentials.getHelpersPath(),
- helper).getPath();
+ if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
+ PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+
+ try {
+ final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
+ if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
+ helperPath = new RemoteFile(remoteSdkCredentials.getHelpersPath(),
+ helper).getPath();
+ }
+ else {
+ helperPath = null;
+ }
}
- else {
+ catch (Exception e) {
helperPath = null;
+ LOG.error(e);
}
}
else {
@@ -833,25 +841,26 @@
catch (final ExecutionException e) {
if (e.getCause() instanceof VagrantNotStartedException) {
throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
- LAUNCH_VAGRANT +
- "\">Launch vagrant</a>").withHandler(LAUNCH_VAGRANT, new Runnable() {
- @Override
- public void run() {
- final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
- if (manager != null) {
+ LAUNCH_VAGRANT +
+ "\">Launch vagrant</a>")
+ .withHandler(LAUNCH_VAGRANT, new Runnable() {
+ @Override
+ public void run() {
+ final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+ if (manager != null) {
- try {
- manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
- clearCaches();
-
- }
- catch (ExecutionException e1) {
- throw new RuntimeException(e1);
+ try {
+ manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
+ clearCaches();
+ }
+ catch (ExecutionException e1) {
+ throw new RuntimeException(e1);
+ }
}
}
- }
- });
- } else {
+ });
+ }
+ else {
throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
}
}
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageUtil.java b/python/src/com/jetbrains/python/packaging/PyPackageUtil.java
index 3bfcc49..31258fa 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageUtil.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageUtil.java
@@ -181,7 +181,7 @@
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
- if (!fileIndex.isIgnored(file) && file.isDirectory() && file.findChild(PyNames.INIT_DOT_PY) != null) {
+ if (!fileIndex.isExcluded(file) && file.isDirectory() && file.findChild(PyNames.INIT_DOT_PY) != null) {
results.add(VfsUtilCore.getRelativePath(file, root, '.'));
}
return true;
diff --git a/python/src/com/jetbrains/python/projectView/PyRemoteLibrariesNode.java b/python/src/com/jetbrains/python/projectView/PyRemoteLibrariesNode.java
index 62de7ea..c5b0af7 100644
--- a/python/src/com/jetbrains/python/projectView/PyRemoteLibrariesNode.java
+++ b/python/src/com/jetbrains/python/projectView/PyRemoteLibrariesNode.java
@@ -19,16 +19,16 @@
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
+import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiDirectory;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiManager;
+import com.intellij.psi.*;
import com.intellij.util.PlatformIcons;
import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
import com.jetbrains.python.sdk.PySdkUtil;
@@ -75,11 +75,16 @@
return FluentIterable.from(Lists.newArrayList(getValue().getChildren())).transform(new Function<PsiElement, AbstractTreeNode>() {
@Override
public AbstractTreeNode apply(PsiElement input) {
- if (input instanceof PsiDirectory) {
- PsiDirectory directory = (PsiDirectory)input;
- if (myRemoteSdkData.getPathMappings().canReplaceLocal((directory.getVirtualFile().getPath()))) {
- return new PyRemoteRootNode(myRemoteSdkData.getPathMappings().convertToRemote(directory.getVirtualFile().getPath()),
- getProject(), directory, getSettings());
+ if (input instanceof PsiFileSystemItem) {
+ String path = ((PsiFileSystemItem)input).getVirtualFile().getPath();
+
+
+ PsiDirectory dir = input instanceof PsiDirectory ? (PsiDirectory)input : getDirectoryForJar((PsiFile)input);
+
+
+ if (myRemoteSdkData.getPathMappings().canReplaceLocal(path)) {
+ return new PyRemoteRootNode(myRemoteSdkData.getPathMappings().convertToRemote(path),
+ getProject(), dir, getSettings());
}
}
@@ -88,6 +93,26 @@
}).filter(Predicates.notNull()).toList();
}
+ @Nullable
+ private PsiDirectory getDirectoryForJar(PsiFile input) {
+ VirtualFile jarRoot = getJarRoot(input);
+ if (myProject != null && jarRoot != null) {
+ return PsiManager.getInstance(myProject).findDirectory(jarRoot);
+ }
+ else {
+ return null;
+ }
+ }
+
+ @Nullable
+ private static VirtualFile getJarRoot(PsiFile input) {
+ final VirtualFile file = input.getVirtualFile();
+ if (file == null || !file.isValid() || !(file.getFileType() instanceof ArchiveFileType)) {
+ return null;
+ }
+ return JarFileSystem.getInstance().getJarRootForLocalFile(file);
+ }
+
public static class PyRemoteRootNode extends PsiDirectoryNode {
private String myRemotePath;
diff --git a/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java b/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
index 6153bc6..8e48894 100644
--- a/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
+++ b/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
@@ -28,6 +28,8 @@
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.JdkOrderEntry;
import com.intellij.openapi.roots.LibraryOrSdkOrderEntry;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -50,7 +52,9 @@
public class PyTreeStructureProvider implements SelectableTreeStructureProvider, DumbAware {
@NotNull
@Override
- public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> children, ViewSettings settings) {
+ public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
+ @NotNull Collection<AbstractTreeNode> children,
+ ViewSettings settings) {
final Project project = parent.getProject();
final Sdk sdk = getPythonSdk(parent);
if (sdk != null && project != null) {
@@ -111,7 +115,15 @@
if (directory.getVirtualFile().equals(PyUserSkeletonsUtil.getUserSkeletonsDirectory())) {
continue;
}
- PsiDirectory dirParent = directory.getParent();
+ VirtualFile dir = directory.getVirtualFile();
+ if (dir.getFileSystem() instanceof JarFileSystem) {
+ dir = ((JarFileSystem)directory.getVirtualFile().getFileSystem()).getLocalVirtualFileFor(directory.getVirtualFile());
+ }
+ if (dir == null) {
+ continue;
+ }
+ VirtualFile dirParent = dir.getParent();
+
if (dirParent != null && dirParent.getName().equals(PythonSdkType.SKELETON_DIR_NAME)) {
continue;
}
@@ -120,7 +132,7 @@
continue;
}
if (dirParent != null) {
- PsiDirectory grandParent = dirParent.getParent();
+ VirtualFile grandParent = dirParent.getParent();
if (grandParent != null && grandParent.getName().equals(PythonSdkType.REMOTE_SOURCES_DIR_NAME)) {
continue;
@@ -158,8 +170,8 @@
}
}
if (parents.size() > 0) {
- return parents.get(parents.size()-1);
+ return parents.get(parents.size() - 1);
}
- return element.getContainingFile();
+ return element.getContainingFile();
}
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
index 307ac8e..4011c23 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
@@ -15,6 +15,8 @@
*/
package com.jetbrains.python.psi.impl;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.psi.PsiElement;
@@ -50,7 +52,11 @@
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
if (source instanceof PyFile) {
- chain.add(((PyFile) source).iterateNames());
+ Iterable<PyElement> declaredNames = ((PyFile)source).iterateNames();
+ if (((PyFile)source).getDunderAll() == null) {
+ declaredNames = excludeUnderscoredNames(declaredNames);
+ }
+ chain.add(declaredNames);
}
}
return chain;
@@ -58,6 +64,19 @@
return Collections.emptyList();
}
+ private static Iterable<PyElement> excludeUnderscoredNames(Iterable<PyElement> declaredNames) {
+ return Iterables.filter(declaredNames, new Predicate<PyElement>() {
+ @Override
+ public boolean apply(@Nullable PyElement input) {
+ final String name = input != null ? input.getName() : null;
+ if (name != null && name.startsWith("_")) {
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+
@Nullable
public PsiElement getElementNamed(final String name) {
if (PyUtil.isClassPrivateName(name)) {
@@ -76,7 +95,7 @@
final PsiElement result = results != null && !results.isEmpty() ? results.get(0).getElement() : null;
if (result != null) {
final List<String> all = sourceFile.getDunderAll();
- if (all != null && !all.contains(name)) {
+ if (all != null ? !all.contains(name) : name.startsWith("_")) {
continue;
}
return result;
diff --git a/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java b/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
index 136a707..7e15cab 100644
--- a/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
+++ b/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
@@ -146,7 +146,7 @@
private static final FileAttribute PERSISTENCE = new FileAttribute("python_language_level_persistence", 2, true);
- public void persistAttribute(@NotNull VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
+ public void persistAttribute(@NotNull Project project, @NotNull VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
final DataInputStream iStream = PERSISTENCE.readAttribute(fileOrDir);
if (iStream != null) {
try {
@@ -164,7 +164,7 @@
for (VirtualFile child : fileOrDir.getChildren()) {
if (!child.isDirectory() && PythonFileType.INSTANCE.equals(child.getFileType())) {
- PushedFilePropertiesUpdater.filePropertiesChanged(child);
+ PushedFilePropertiesUpdater.getInstance(project).filePropertiesChanged(child);
}
}
}
@@ -247,7 +247,7 @@
return false;
}
if (file.isDirectory()) {
- PushedFilePropertiesUpdater.findAndUpdateValue(project, file, PythonLanguageLevelPusher.this, languageLevel);
+ PushedFilePropertiesUpdater.getInstance(project).findAndUpdateValue(file, PythonLanguageLevelPusher.this, languageLevel);
}
if (suppressSizeLimit) {
SingleRootFileViewProvider.doNotCheckFileSizeLimit(file);
diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java
index 15a3421..0858ff5 100644
--- a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java
+++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
* @author yole
*/
public class PyFileStubBuilder extends DefaultStubBuilder {
+ @NotNull
@Override
protected StubElement createStubForFile(@NotNull PsiFile file) {
if (file instanceof PyFile) {
diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
index bb58b68..4b3adfd 100644
--- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
@@ -16,8 +16,11 @@
package com.jetbrains.python.psi.types;
import com.intellij.psi.PsiElement;
+import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
+import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.NotNull;
@@ -71,12 +74,20 @@
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext resolveContext) {
- return Collections.emptyList();
+ final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
+ if (functionType == null) {
+ return Collections.emptyList();
+ }
+ return functionType.resolveMember(name, location, direction, resolveContext);
}
@Override
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
- return new Object[0];
+ final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
+ if (functionType == null) {
+ return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ }
+ return functionType.getCompletionVariants(completionPrefix, location, context);
}
@Override
diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
index f2dc67c..db5a9ba 100644
--- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
@@ -308,7 +308,7 @@
return !(psiElement instanceof PyImportElement) ||
PsiTreeUtil.getParentOfType(psiElement, PyImportStatementBase.class) instanceof PyFromImportStatement;
}
- }, new PyUtil.UnderscoreFilter(0));
+ }, null);
if (suppressParentheses) {
processor.suppressParentheses();
}
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
index 5e3506b..f194f35 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
@@ -56,7 +56,9 @@
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.reference.SoftReference;
-import com.intellij.remote.*;
+import com.intellij.remote.CredentialsType;
+import com.intellij.remote.RemoteSdkCredentialsHolder;
+import com.intellij.remote.VagrantNotStartedException;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.NullableConsumer;
@@ -993,9 +995,9 @@
}
public static boolean isIncompleteRemote(Sdk sdk) {
- if (sdk.getSdkAdditionalData() instanceof RemoteSdkCredentials) {
+ if (PySdkUtil.isRemote(sdk)) {
//noinspection ConstantConditions
- if (!((RemoteSdkCredentials)sdk.getSdkAdditionalData()).isInitialized()) {
+ if (!((PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData()).isInitialized()) {
return true;
}
}
diff --git a/python/src/com/jetbrains/python/sdk/flavors/PyRemoteSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/PyRemoteSdkFlavor.java
index 271afc6..1240031 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/PyRemoteSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/PyRemoteSdkFlavor.java
@@ -57,6 +57,6 @@
@Nullable
private static String getExecutableName(String path) {
- return RemoteFile.detectSystemByPath(path).createRemoteFile(path).getName();
+ return RemoteFile.createRemoteFile(path).getName();
}
}
diff --git a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
index c115ba4..a7faa6e 100644
--- a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
@@ -145,7 +145,7 @@
}
final PyPep8Inspection inspection = (PyPep8Inspection)profile.getUnwrappedTool(PyPep8Inspection.KEY.toString(), file);
final List<String> ignoredErrors = inspection.ignoredErrors;
- final int margin = CodeStyleSettingsManager.getInstance(file.getProject()).getCurrentSettings().RIGHT_MARGIN;
+ final int margin = CodeStyleSettingsManager.getInstance(file.getProject()).getCurrentSettings().getRightMargin(file.getLanguage());
return new State(homePath, file.getText(), profile.getErrorLevel(key, file), ignoredErrors, margin);
}
diff --git a/python/testData/MockSdk2.7/python_stubs/__builtin__.py b/python/testData/MockSdk2.7/python_stubs/__builtin__.py
index 6ae5948..ca174bb 100644
--- a/python/testData/MockSdk2.7/python_stubs/__builtin__.py
+++ b/python/testData/MockSdk2.7/python_stubs/__builtin__.py
@@ -1,7 +1,7 @@
# encoding: utf-8
# module __builtin__
# from (built-in)
-# by generator 1.124
+# by generator 1.135
from __future__ import print_function
"""
Built-in functions, exceptions, and other objects.
@@ -43,25 +43,24 @@
"""
return 0
-
def all(iterable): # real signature unknown; restored from __doc__
"""
all(iterable) -> bool
Return True if bool(x) is True for all values x in the iterable.
+ If the iterable is empty, return True.
"""
return False
-
def any(iterable): # real signature unknown; restored from __doc__
"""
any(iterable) -> bool
Return True if bool(x) is True for any x in the iterable.
+ If the iterable is empty, return False.
"""
return False
-
def apply(p_object, args=None, kwargs=None): # real signature unknown; restored from __doc__
"""
apply(object[, args[, kwargs]]) -> value
@@ -75,7 +74,6 @@
"""
pass
-
def bin(number): # real signature unknown; restored from __doc__
"""
bin(number) -> string
@@ -84,7 +82,6 @@
"""
return ""
-
def callable(p_object): # real signature unknown; restored from __doc__
"""
callable(object) -> bool
@@ -94,7 +91,6 @@
"""
return False
-
def chr(i): # real signature unknown; restored from __doc__
"""
chr(i) -> character
@@ -103,7 +99,6 @@
"""
return ""
-
def cmp(x, y): # real signature unknown; restored from __doc__
"""
cmp(x, y) -> integer
@@ -112,7 +107,6 @@
"""
return 0
-
def coerce(x, y): # real signature unknown; restored from __doc__
"""
coerce(x, y) -> (x1, y1)
@@ -123,7 +117,6 @@
"""
pass
-
def compile(source, filename, mode, flags=None, dont_inherit=None): # real signature unknown; restored from __doc__
"""
compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
@@ -142,7 +135,6 @@
"""
pass
-
def copyright(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -150,7 +142,6 @@
"""
pass
-
def credits(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -158,7 +149,6 @@
"""
pass
-
def delattr(p_object, name): # real signature unknown; restored from __doc__
"""
delattr(object, name)
@@ -168,7 +158,6 @@
"""
pass
-
def dir(p_object=None): # real signature unknown; restored from __doc__
"""
dir([object]) -> list of strings
@@ -186,7 +175,6 @@
"""
return []
-
def divmod(x, y): # known case of __builtin__.divmod
"""
divmod(x, y) -> (quotient, remainder)
@@ -195,7 +183,6 @@
"""
return (0, 0)
-
def eval(source, globals=None, locals=None): # real signature unknown; restored from __doc__
"""
eval(source[, globals[, locals]]) -> value
@@ -209,7 +196,6 @@
"""
pass
-
def execfile(filename, globals=None, locals=None): # real signature unknown; restored from __doc__
"""
execfile(filename[, globals[, locals]])
@@ -220,11 +206,9 @@
"""
pass
-
def exit(*args, **kwargs): # real signature unknown
pass
-
def filter(function_or_none, sequence): # known special case of filter
"""
filter(function or None, sequence) -> list, tuple, or string
@@ -235,7 +219,6 @@
"""
pass
-
def format(value, format_spec=None): # real signature unknown; restored from __doc__
"""
format(value[, format_spec]) -> string
@@ -245,7 +228,6 @@
"""
return ""
-
def getattr(object, name, default=None): # known special case of getattr
"""
getattr(object, name[, default]) -> value
@@ -256,7 +238,6 @@
"""
pass
-
def globals(): # real signature unknown; restored from __doc__
"""
globals() -> dictionary
@@ -265,7 +246,6 @@
"""
return {}
-
def hasattr(p_object, name): # real signature unknown; restored from __doc__
"""
hasattr(object, name) -> bool
@@ -275,7 +255,6 @@
"""
return False
-
def hash(p_object): # real signature unknown; restored from __doc__
"""
hash(object) -> integer
@@ -285,7 +264,6 @@
"""
return 0
-
def help(with_a_twist): # real signature unknown; restored from __doc__
"""
Define the builtin 'help'.
@@ -293,7 +271,6 @@
"""
pass
-
def hex(number): # real signature unknown; restored from __doc__
"""
hex(number) -> string
@@ -302,7 +279,6 @@
"""
return ""
-
def id(p_object): # real signature unknown; restored from __doc__
"""
id(object) -> integer
@@ -312,7 +288,6 @@
"""
return 0
-
def input(prompt=None): # real signature unknown; restored from __doc__
"""
input([prompt]) -> value
@@ -321,7 +296,6 @@
"""
pass
-
def intern(string): # real signature unknown; restored from __doc__
"""
intern(string) -> string
@@ -333,7 +307,6 @@
"""
return ""
-
def isinstance(p_object, class_or_type_or_tuple): # real signature unknown; restored from __doc__
"""
isinstance(object, class-or-type-or-tuple) -> bool
@@ -345,7 +318,6 @@
"""
return False
-
def issubclass(C, B): # real signature unknown; restored from __doc__
"""
issubclass(C, B) -> bool
@@ -356,7 +328,6 @@
"""
return False
-
def iter(source, sentinel=None): # known special case of iter
"""
iter(collection) -> iterator
@@ -368,7 +339,6 @@
"""
pass
-
def len(p_object): # real signature unknown; restored from __doc__
"""
len(object) -> integer
@@ -377,7 +347,6 @@
"""
return 0
-
def license(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -385,7 +354,6 @@
"""
pass
-
def locals(): # real signature unknown; restored from __doc__
"""
locals() -> dictionary
@@ -394,7 +362,6 @@
"""
return {}
-
def map(function, sequence, *sequence_1): # real signature unknown; restored from __doc__
"""
map(function, sequence[, sequence, ...]) -> list
@@ -408,7 +375,6 @@
"""
return []
-
def max(*args, **kwargs): # known special case of max
"""
max(iterable[, key=func]) -> value
@@ -419,7 +385,6 @@
"""
pass
-
def min(*args, **kwargs): # known special case of min
"""
min(iterable[, key=func]) -> value
@@ -430,7 +395,6 @@
"""
pass
-
def next(iterator, default=None): # real signature unknown; restored from __doc__
"""
next(iterator[, default])
@@ -440,7 +404,6 @@
"""
pass
-
def oct(number): # real signature unknown; restored from __doc__
"""
oct(number) -> string
@@ -449,7 +412,6 @@
"""
return ""
-
def open(name, mode=None, buffering=None): # real signature unknown; restored from __doc__
"""
open(name[, mode[, buffering]]) -> file object
@@ -459,7 +421,6 @@
"""
return file('/dev/null')
-
def ord(c): # real signature unknown; restored from __doc__
"""
ord(c) -> integer
@@ -468,7 +429,6 @@
"""
return 0
-
def pow(x, y, z=None): # real signature unknown; restored from __doc__
"""
pow(x, y[, z]) -> number
@@ -478,7 +438,6 @@
"""
return 0
-
def print(*args, **kwargs): # known special case of print
"""
print(value, ..., sep=' ', end='\n', file=sys.stdout)
@@ -491,14 +450,13 @@
"""
pass
-
def quit(*args, **kwargs): # real signature unknown
pass
-
def range(start=None, stop=None, step=None): # known special case of range
"""
- range([start,] stop[, step]) -> list of integers
+ range(stop) -> list of integers
+ range(start, stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
@@ -508,7 +466,6 @@
"""
pass
-
def raw_input(prompt=None): # real signature unknown; restored from __doc__
"""
raw_input([prompt]) -> string
@@ -520,7 +477,6 @@
"""
return ""
-
def reduce(function, sequence, initial=None): # real signature unknown; restored from __doc__
"""
reduce(function, sequence[, initial]) -> value
@@ -534,7 +490,6 @@
"""
pass
-
def reload(module): # real signature unknown; restored from __doc__
"""
reload(module) -> module
@@ -543,7 +498,6 @@
"""
pass
-
def repr(p_object): # real signature unknown; restored from __doc__
"""
repr(object) -> string
@@ -553,7 +507,6 @@
"""
return ""
-
def round(number, ndigits=None): # real signature unknown; restored from __doc__
"""
round(number[, ndigits]) -> floating point number
@@ -563,7 +516,6 @@
"""
return 0.0
-
def setattr(p_object, name, value): # real signature unknown; restored from __doc__
"""
setattr(object, name, value)
@@ -573,23 +525,20 @@
"""
pass
-
def sorted(iterable, cmp=None, key=None, reverse=False): # real signature unknown; restored from __doc__
""" sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list """
pass
-
def sum(sequence, start=None): # real signature unknown; restored from __doc__
"""
sum(sequence[, start]) -> value
- Returns the sum of a sequence of numbers (NOT strings) plus the value
+ Return the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
- empty, returns start.
+ empty, return start.
"""
pass
-
def unichr(i): # real signature unknown; restored from __doc__
"""
unichr(i) -> Unicode character
@@ -598,7 +547,6 @@
"""
return u""
-
def vars(p_object=None): # real signature unknown; restored from __doc__
"""
vars([object]) -> dictionary
@@ -608,7 +556,6 @@
"""
return {}
-
def zip(seq1, seq2, *more_seqs): # known special case of zip
"""
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
@@ -619,13 +566,16 @@
"""
pass
-
def __import__(name, globals={}, locals={}, fromlist=[], level=-1): # real signature unknown; restored from __doc__
"""
__import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module
- Import a module. The globals are only used to determine the context;
- they are not modified. The locals are currently unused. The fromlist
+ Import a module. Because this function is meant for use by the Python
+ interpreter and not for general use it is better to use
+ importlib.import_module() to programmatically import a module.
+
+ The globals argument is only used to determine the context;
+ they are not modified. The locals argument is unused. The fromlist
should be a list of names to emulate ``from name import ...'', or an
empty list to emulate ``import name''.
When importing a module from a package, note that __import__('A.B', ...)
@@ -637,9 +587,100 @@
"""
pass
-
# classes
+class ___Classobj:
+ '''A mock class representing the old style class base.'''
+ __module__ = ''
+ __class__ = None
+
+ def __init__(self):
+ pass
+ __dict__ = {}
+ __doc__ = ''
+
+
+class __generator(object):
+ '''A mock class representing the generator function type.'''
+ def __init__(self):
+ self.gi_code = None
+ self.gi_frame = None
+ self.gi_running = 0
+
+ def __iter__(self):
+ '''Defined to support iteration over container.'''
+ pass
+
+ def next(self):
+ '''Return the next item from the container.'''
+ pass
+
+ def close(self):
+ '''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
+ pass
+
+ def send(self, value):
+ '''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.'''
+ pass
+
+ def throw(self, type, value=None, traceback=None):
+ '''Used to raise an exception inside the generator.'''
+ pass
+
+
+class __function(object):
+ '''A mock class representing function type.'''
+
+ def __init__(self):
+ self.__name__ = ''
+ self.__doc__ = ''
+ self.__dict__ = ''
+ self.__module__ = ''
+
+ self.func_defaults = {}
+ self.func_globals = {}
+ self.func_closure = None
+ self.func_code = None
+ self.func_name = ''
+ self.func_doc = ''
+ self.func_dict = ''
+
+ self.__defaults__ = {}
+ self.__globals__ = {}
+ self.__closure__ = None
+ self.__code__ = None
+ self.__name__ = ''
+
+
+class __namedtuple(tuple):
+ '''A mock base class for named tuples.'''
+
+ __slots__ = ()
+ _fields = ()
+
+ def __new__(cls, *args, **kwargs):
+ 'Create a new instance of the named tuple.'
+ return tuple.__new__(cls, *args)
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new named tuple object from a sequence or iterable.'
+ return new(cls, iterable)
+
+ def __repr__(self):
+ return ''
+
+ def _asdict(self):
+ 'Return a new dict which maps field types to their values.'
+ return {}
+
+ def _replace(self, **kwargs):
+ 'Return a new named tuple object replacing specified fields with new values.'
+ return self
+
+ def __getnewargs__(self):
+ return tuple(self)
+
class object:
""" The most base type """
def __delattr__(self, name): # real signature unknown; restored from __doc__
@@ -712,6 +753,8 @@
__module__ = ''
+from object import object
+
class basestring(object):
""" Type basestring cannot be instantiated; it is the base for str and unicode. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -723,17 +766,24 @@
pass
+from object import object
+
class int(object):
"""
- int(x[, base]) -> integer
+ int(x=0) -> int or long
+ int(x, base=10) -> int or long
- Convert a string or number to an integer, if possible. A floating point
- argument will be truncated towards zero (this does not include a string
- representation of a floating point number!) When converting a string, use
- the optional base. It is an error to supply a base when converting a
- non-string. If base is zero, the proper base is guessed based on the
- string content. If the argument is outside the integer range a
- long object will be returned instead.
+ Convert a number or string to an integer, or return 0 if no arguments
+ are given. If x is floating point, the conversion truncates towards zero.
+ If x is outside the integer range, the function returns a long instead.
+
+ If x is not a number or if base is given, then x must be a string or
+ Unicode object representing an integer literal in the given base. The
+ literal can be preceded by '+' or '-' and be surrounded by whitespace.
+ The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to
+ interpret the base from the string as an integer literal.
+ >>> int('0b100', base=0)
+ 4
"""
def bit_length(self): # real signature unknown; restored from __doc__
"""
@@ -811,15 +861,20 @@
def __init__(self, x, base=10): # known special case of int.__init__
"""
- int(x[, base]) -> integer
+ int(x=0) -> int or long
+ int(x, base=10) -> int or long
- Convert a string or number to an integer, if possible. A floating point
- argument will be truncated towards zero (this does not include a string
- representation of a floating point number!) When converting a string, use
- the optional base. It is an error to supply a base when converting a
- non-string. If base is zero, the proper base is guessed based on the
- string content. If the argument is outside the integer range a
- long object will be returned instead.
+ Convert a number or string to an integer, or return 0 if no arguments
+ are given. If x is floating point, the conversion truncates towards zero.
+ If x is outside the integer range, the function returns a long instead.
+
+ If x is not a number or if base is given, then x must be a string or
+ Unicode object representing an integer literal in the given base. The
+ literal can be preceded by '+' or '-' and be surrounded by whitespace.
+ The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to
+ interpret the base from the string as an integer literal.
+ >>> int('0b100', base=0)
+ 4
# (copied from class doc)
"""
pass
@@ -961,11 +1016,21 @@
""" x.__xor__(y) <==> x^y """
pass
- denominator = property(lambda self: object()) # default
- imag = property(lambda self: object()) # default
- numerator = property(lambda self: object()) # default
- real = property(lambda self: object()) # default
+ denominator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the denominator of a rational number in lowest terms"""
+ imag = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the imaginary part of a complex number"""
+
+ numerator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the numerator of a rational number in lowest terms"""
+
+ real = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the real part of a complex number"""
+
+
+
+from int import int
class bool(int):
"""
@@ -1016,6 +1081,8 @@
pass
+from object import object
+
class buffer(object):
"""
buffer(object [, offset[, size]])
@@ -1106,6 +1173,8 @@
pass
+from object import object
+
class bytearray(object):
"""
bytearray(iterable_of_ints) -> bytearray.
@@ -1456,9 +1525,9 @@
"""
return []
- def splitlines(self, keepends=None): # real signature unknown; restored from __doc__
+ def splitlines(self, keepends=False): # real signature unknown; restored from __doc__
"""
- B.splitlines([keepends]) -> list of lines
+ B.splitlines(keepends=False) -> list of lines
Return a list of the lines in B, breaking at line boundaries.
Line breaks are not included in the resulting list unless keepends
@@ -1658,9 +1727,11 @@
pass
+from basestring import basestring
+
class str(basestring):
"""
- str(object) -> string
+ str(object='') -> string
Return a nice string representation of the object.
If the argument is a string, the return value is the same object.
@@ -1962,9 +2033,9 @@
"""
return []
- def splitlines(self, keepends=None): # real signature unknown; restored from __doc__
+ def splitlines(self, keepends=False): # real signature unknown; restored from __doc__
"""
- S.splitlines([keepends]) -> list of strings
+ S.splitlines(keepends=False) -> list of strings
Return a list of the lines in S, breaking at line boundaries.
Line breaks are not included in the resulting list unless keepends
@@ -2101,7 +2172,7 @@
def __init__(self, string=''): # known special case of str.__init__
"""
- str(object) -> string
+ str(object='') -> string
Return a nice string representation of the object.
If the argument is a string, the return value is the same object.
@@ -2162,6 +2233,8 @@
bytes = str
+from object import object
+
class classmethod(object):
"""
classmethod(function) -> method
@@ -2200,9 +2273,12 @@
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
- __func__ = property(lambda self: object()) # default
+ __func__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+from object import object
+
class complex(object):
"""
complex(real[, imag]) -> complex number
@@ -2214,7 +2290,7 @@
"""
complex.conjugate() -> complex
- Returns the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.
+ Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.
"""
return complex
@@ -2254,7 +2330,7 @@
"""
complex.__format__() -> str
- Converts to a string according to format_spec.
+ Convert to a string according to format_spec.
"""
return ""
@@ -2382,8 +2458,20 @@
pass
imag = property(lambda self: 0.0)
- real = property(lambda self: 0.0)
+ """the imaginary part of a complex number
+ :type: float
+ """
+
+ real = property(lambda self: 0.0)
+ """the real part of a complex number
+
+ :type: float
+ """
+
+
+
+from object import object
class dict(object):
"""
@@ -2461,9 +2549,9 @@
def update(self, E=None, **F): # known special case of dict.update
"""
- D.update(E, **F) -> None. Update D from dict/iterable E and F.
- If E has a .keys() method, does: for k in E: D[k] = E[k]
- If E lacks .keys() method, does: for (k, v) in E: D[k] = v
+ D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
+ If E present and has a .keys() method, does: for k in E: D[k] = E[k]
+ If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
"""
pass
@@ -2571,6 +2659,8 @@
__hash__ = None
+from object import object
+
class enumerate(object):
"""
enumerate(iterable[, start]) -> iterator for index, value of iterable
@@ -2603,6 +2693,8 @@
pass
+from object import object
+
class file(object):
"""
file(name[, mode[, buffering]]) -> file object
@@ -2779,13 +2871,47 @@
pass
closed = property(lambda self: True)
- encoding = property(lambda self: '')
- errors = property(lambda self: object()) # default
- mode = property(lambda self: '')
- name = property(lambda self: '')
- newlines = property(lambda self: '')
- softspace = property(lambda self: True)
+ """True if the file is closed
+ :type: bool
+ """
+
+ encoding = property(lambda self: '')
+ """file encoding
+
+ :type: string
+ """
+
+ errors = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """Unicode error handler"""
+
+ mode = property(lambda self: '')
+ """file mode ('r', 'U', 'w', 'a', possibly with 'b' or '+' added)
+
+ :type: string
+ """
+
+ name = property(lambda self: '')
+ """file name
+
+ :type: string
+ """
+
+ newlines = property(lambda self: '')
+ """end-of-line convention used in this file
+
+ :type: string
+ """
+
+ softspace = property(lambda self: True)
+ """flag indicating that a space needs to be printed; used by print
+
+ :type: bool
+ """
+
+
+
+from object import object
class float(object):
"""
@@ -2797,9 +2923,9 @@
"""
float.as_integer_ratio() -> (int, int)
- Returns a pair of integers, whose ratio is exactly equal to the original
+ Return a pair of integers, whose ratio is exactly equal to the original
float and with a positive denominator.
- Raises OverflowError on infinities and a ValueError on NaNs.
+ Raise OverflowError on infinities and a ValueError on NaNs.
>>> (10.0).as_integer_ratio()
(10, 1)
@@ -2811,7 +2937,7 @@
pass
def conjugate(self, *args, **kwargs): # real signature unknown
- """ Returns self, the complex conjugate of any float. """
+ """ Return self, the complex conjugate of any float. """
pass
def fromhex(self, string): # real signature unknown; restored from __doc__
@@ -2839,7 +2965,7 @@
return ""
def is_integer(self, *args, **kwargs): # real signature unknown
- """ Returns True if the float is an integer. """
+ """ Return True if the float is an integer. """
pass
def __abs__(self): # real signature unknown; restored from __doc__
@@ -3017,7 +3143,7 @@
'IEEE, big-endian' or 'IEEE, little-endian', and in addition can only be
one of the latter two if it appears to match the underlying C reality.
- Overrides the automatic determination of C-level floating point type.
+ Override the automatic determination of C-level floating point type.
This affects how floats are converted to and from binary strings.
"""
pass
@@ -3035,12 +3161,18 @@
pass
def __trunc__(self, *args, **kwargs): # real signature unknown
- """ Returns the Integral closest to x between 0 and x. """
+ """ Return the Integral closest to x between 0 and x. """
pass
- imag = property(lambda self: object()) # default
- real = property(lambda self: object()) # default
+ imag = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the imaginary part of a complex number"""
+ real = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the real part of a complex number"""
+
+
+
+from object import object
class frozenset(object):
"""
@@ -3199,6 +3331,8 @@
pass
+from object import object
+
class list(object):
"""
list() -> new empty list
@@ -3376,15 +3510,23 @@
__hash__ = None
+from object import object
+
class long(object):
"""
- long(x[, base]) -> integer
+ long(x=0) -> long
+ long(x, base=10) -> long
- Convert a string or number to a long integer, if possible. A floating
- point argument will be truncated towards zero (this does not include a
- string representation of a floating point number!) When converting a
- string, use the optional base. It is an error to supply a base when
- converting a non-string.
+ Convert a number or string to a long integer, or return 0L if no arguments
+ are given. If x is floating point, the conversion truncates towards zero.
+
+ If x is not a number or if base is given, then x must be a string or
+ Unicode object representing an integer literal in the given base. The
+ literal can be preceded by '+' or '-' and be surrounded by whitespace.
+ The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to
+ interpret the base from the string as an integer literal.
+ >>> int('0b100', base=0)
+ 4L
"""
def bit_length(self): # real signature unknown; restored from __doc__
"""
@@ -3460,7 +3602,7 @@
""" x[y:z] <==> x[y.__index__():z.__index__()] """
pass
- def __init__(self, x, base=None): # real signature unknown; restored from __doc__
+ def __init__(self, x=0): # real signature unknown; restored from __doc__
pass
def __int__(self): # real signature unknown; restored from __doc__
@@ -3604,11 +3746,21 @@
""" x.__xor__(y) <==> x^y """
pass
- denominator = property(lambda self: object()) # default
- imag = property(lambda self: object()) # default
- numerator = property(lambda self: object()) # default
- real = property(lambda self: object()) # default
+ denominator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the denominator of a rational number in lowest terms"""
+ imag = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the imaginary part of a complex number"""
+
+ numerator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the numerator of a rational number in lowest terms"""
+
+ real = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the real part of a complex number"""
+
+
+
+from object import object
class memoryview(object):
"""
@@ -3678,14 +3830,23 @@
""" x.__setitem__(i, y) <==> x[i]=y """
pass
- format = property(lambda self: object()) # default
- itemsize = property(lambda self: object()) # default
- ndim = property(lambda self: object()) # default
- readonly = property(lambda self: object()) # default
- shape = property(lambda self: object()) # default
- strides = property(lambda self: object()) # default
- suboffsets = property(lambda self: object()) # default
+ format = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ itemsize = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ ndim = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ readonly = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ shape = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ strides = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ suboffsets = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+
+
+from object import object
class property(object):
"""
@@ -3694,6 +3855,7 @@
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
+
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
@@ -3701,13 +3863,18 @@
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
+
class C(object):
@property
- def x(self): return self._x
+ def x(self):
+ "I am the 'x' property."
+ return self._x
@x.setter
- def x(self, value): self._x = value
+ def x(self, value):
+ self._x = value
@x.deleter
- def x(self): del self._x
+ def x(self):
+ del self._x
"""
def deleter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the deleter on a property. """
@@ -3740,6 +3907,7 @@
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
+
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
@@ -3747,13 +3915,18 @@
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
+
class C(object):
@property
- def x(self): return self._x
+ def x(self):
+ "I am the 'x' property."
+ return self._x
@x.setter
- def x(self, value): self._x = value
+ def x(self, value):
+ self._x = value
@x.deleter
- def x(self): del self._x
+ def x(self):
+ del self._x
# (copied from class doc)
"""
@@ -3768,10 +3941,15 @@
""" descr.__set__(obj, value) """
pass
- fdel = property(lambda self: object()) # default
- fget = property(lambda self: object()) # default
- fset = property(lambda self: object()) # default
+ fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+
+
+from object import object
class reversed(object):
"""
@@ -3804,6 +3982,8 @@
pass
+from object import object
+
class set(object):
"""
set() -> new empty set object
@@ -3939,7 +4119,7 @@
pass
def __iand__(self, y): # real signature unknown; restored from __doc__
- """ x.__iand__(y) <==> x&y """
+ """ x.__iand__(y) <==> x&=y """
pass
def __init__(self, seq=()): # known special case of set.__init__
@@ -3953,11 +4133,11 @@
pass
def __ior__(self, y): # real signature unknown; restored from __doc__
- """ x.__ior__(y) <==> x|y """
+ """ x.__ior__(y) <==> x|=y """
pass
def __isub__(self, y): # real signature unknown; restored from __doc__
- """ x.__isub__(y) <==> x-y """
+ """ x.__isub__(y) <==> x-=y """
pass
def __iter__(self): # real signature unknown; restored from __doc__
@@ -3965,7 +4145,7 @@
pass
def __ixor__(self, y): # real signature unknown; restored from __doc__
- """ x.__ixor__(y) <==> x^y """
+ """ x.__ixor__(y) <==> x^=y """
pass
def __len__(self): # real signature unknown; restored from __doc__
@@ -4032,9 +4212,12 @@
__hash__ = None
+from object import object
+
class slice(object):
"""
- slice([start,] stop[, step])
+ slice(stop)
+ slice(start, stop[, step])
Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).
"""
@@ -4061,7 +4244,7 @@
""" x.__hash__() <==> hash(x) """
pass
- def __init__(self, start=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
+ def __init__(self, stop): # real signature unknown; restored from __doc__
pass
@staticmethod # known case of __new__
@@ -4078,9 +4261,17 @@
pass
start = property(lambda self: 0)
- step = property(lambda self: 0)
- stop = property(lambda self: 0)
+ """:type: int"""
+ step = property(lambda self: 0)
+ """:type: int"""
+
+ stop = property(lambda self: 0)
+ """:type: int"""
+
+
+
+from object import object
class staticmethod(object):
"""
@@ -4117,13 +4308,16 @@
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
- __func__ = property(lambda self: object()) # default
+ __func__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+from object import object
+
class super(object):
"""
- super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
+ super(type) -> unbound super object
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
@@ -4140,8 +4334,8 @@
def __init__(self, type1, type2=None): # known special case of super.__init__
"""
- super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
+ super(type) -> unbound super object
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
@@ -4161,9 +4355,26 @@
pass
__self_class__ = property(lambda self: type(object))
- __self__ = property(lambda self: type(object))
- __thisclass__ = property(lambda self: type(object))
+ """the type of the instance invoking super(); may be None
+ :type: type
+ """
+
+ __self__ = property(lambda self: type(object))
+ """the instance invoking super(); may be None
+
+ :type: type
+ """
+
+ __thisclass__ = property(lambda self: type(object))
+ """the class invoking super()
+
+ :type: type
+ """
+
+
+
+from object import object
class tuple(object):
"""
@@ -4278,6 +4489,8 @@
pass
+from object import object
+
class type(object):
"""
type(object) -> the object's type
@@ -4369,7 +4582,8 @@
""" __subclasses__() -> list of immediate subclasses """
return []
- __abstractmethods__ = property(lambda self: object()) # default
+ __abstractmethods__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
__bases__ = (
object,
@@ -4388,9 +4602,12 @@
__weakrefoffset__ = 368
+from basestring import basestring
+
class unicode(basestring):
"""
- unicode(string [, encoding[, errors]]) -> object
+ unicode(object='') -> unicode object
+ unicode(string[, encoding[, errors]]) -> unicode object
Create a new Unicode object from the given encoded string.
encoding defaults to the current default string encoding.
@@ -4432,7 +4649,7 @@
to the default encoding. errors may be given to set a different error
handling scheme. Default is 'strict' meaning that encoding errors raise
a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
- as well as any other name registerd with codecs.register_error that is
+ as well as any other name registered with codecs.register_error that is
able to handle UnicodeDecodeErrors.
"""
return ""
@@ -4711,9 +4928,9 @@
"""
return []
- def splitlines(self, keepends=None): # real signature unknown; restored from __doc__
+ def splitlines(self, keepends=False): # real signature unknown; restored from __doc__
"""
- S.splitlines([keepends]) -> list of strings
+ S.splitlines(keepends=False) -> list of strings
Return a list of the lines in S, breaking at line boundaries.
Line breaks are not included in the resulting list unless keepends
@@ -4849,7 +5066,8 @@
def __init__(self, string=u'', encoding=None, errors='strict'): # known special case of unicode.__init__
"""
- unicode(string [, encoding[, errors]]) -> object
+ unicode(object='') -> unicode object
+ unicode(string[, encoding[, errors]]) -> unicode object
Create a new Unicode object from the given encoded string.
encoding defaults to the current default string encoding.
@@ -4908,9 +5126,12 @@
pass
+from object import object
+
class xrange(object):
"""
- xrange([start,] stop[, step]) -> xrange object
+ xrange(stop) -> xrange object
+ xrange(start, stop[, step]) -> xrange object
Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand. For looping, this is
@@ -4924,7 +5145,7 @@
""" x.__getitem__(y) <==> x[y] """
pass
- def __init__(self, start=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
+ def __init__(self, stop): # real signature unknown; restored from __doc__
pass
def __iter__(self): # real signature unknown; restored from __doc__
@@ -4952,74 +5173,6 @@
pass
-class ___Classobj:
- '''A mock class representing the old style class base.'''
- __module__ = ''
- __class__ = None
-
- def __init__(self):
- pass
- __dict__ = {}
- __doc__ = ''
-
-
-class __generator(object):
- '''A mock class representing the generator function type.'''
- def __init__(self):
- self.gi_code = None
- self.gi_frame = None
- self.gi_running = 0
-
- def __iter__(self):
- '''Defined to support iteration over container.'''
- pass
-
- def next(self):
- '''Return the next item from the container.'''
- pass
-
- def close(self):
- '''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
- pass
-
- def send(self, value):
- '''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.'''
- pass
-
- def throw(self, type, value=None, traceback=None):
- '''Used to raise an exception inside the generator.'''
- pass
-
-
-class __namedtuple(tuple):
- '''A mock base class for named tuples.'''
-
- __slots__ = ()
- _fields = ()
-
- def __new__(cls, *args, **kwargs):
- 'Create a new instance of the named tuple.'
- return tuple.__new__(cls, *args)
-
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new named tuple object from a sequence or iterable.'
- return new(cls, iterable)
-
- def __repr__(self):
- return ''
-
- def _asdict(self):
- 'Return a new dict which maps field types to their values.'
- return {}
-
- def _replace(self, **kwargs):
- 'Return a new named tuple object replacing specified fields with new values.'
- return self
-
- def __getnewargs__(self):
- return tuple(self)
-
# variables with complex values
Ellipsis = None # (!) real value is ''
diff --git a/python/testData/MockSdk3.2/python_stubs/builtins.py b/python/testData/MockSdk3.2/python_stubs/builtins.py
index b3fa88d..88b74ab 100644
--- a/python/testData/MockSdk3.2/python_stubs/builtins.py
+++ b/python/testData/MockSdk3.2/python_stubs/builtins.py
@@ -1,7 +1,7 @@
# encoding: utf-8
# module builtins
# from (built-in)
-# by generator 1.118
+# by generator 1.135
"""
Built-in functions, exceptions, and other objects.
@@ -25,25 +25,24 @@
"""
return 0
-
def all(iterable): # real signature unknown; restored from __doc__
"""
all(iterable) -> bool
Return True if bool(x) is True for all values x in the iterable.
+ If the iterable is empty, return True.
"""
return False
-
def any(iterable): # real signature unknown; restored from __doc__
"""
any(iterable) -> bool
Return True if bool(x) is True for any x in the iterable.
+ If the iterable is empty, return False.
"""
return False
-
def ascii(p_object): # real signature unknown; restored from __doc__
"""
ascii(object) -> string
@@ -55,7 +54,6 @@
"""
return ""
-
def bin(number): # real signature unknown; restored from __doc__
"""
bin(number) -> string
@@ -64,7 +62,6 @@
"""
return ""
-
def callable(p_object): # real signature unknown; restored from __doc__
"""
callable(object) -> bool
@@ -75,16 +72,15 @@
"""
return False
-
def chr(i): # real signature unknown; restored from __doc__
"""
chr(i) -> Unicode character
Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.
+ If 0x10000 <= i, a surrogate pair is returned.
"""
return ""
-
def compile(source, filename, mode, flags=None, dont_inherit=None): # real signature unknown; restored from __doc__
"""
compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
@@ -103,7 +99,6 @@
"""
pass
-
def copyright(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -111,7 +106,6 @@
"""
pass
-
def credits(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -119,7 +113,6 @@
"""
pass
-
def delattr(p_object, name): # real signature unknown; restored from __doc__
"""
delattr(object, name)
@@ -129,7 +122,6 @@
"""
pass
-
def dir(p_object=None): # real signature unknown; restored from __doc__
"""
dir([object]) -> list of strings
@@ -147,7 +139,6 @@
"""
return []
-
def divmod(x, y): # known case of builtins.divmod
"""
divmod(x, y) -> (div, mod)
@@ -156,7 +147,6 @@
"""
return (0, 0)
-
def eval(source, globals=None, locals=None): # real signature unknown; restored from __doc__
"""
eval(source[, globals[, locals]]) -> value
@@ -170,7 +160,6 @@
"""
pass
-
def exec(p_object, globals=None, locals=None): # real signature unknown; restored from __doc__
"""
exec(object[, globals[, locals]])
@@ -182,11 +171,9 @@
"""
pass
-
def exit(*args, **kwargs): # real signature unknown
pass
-
def format(value, format_spec=None): # real signature unknown; restored from __doc__
"""
format(value[, format_spec]) -> string
@@ -196,7 +183,6 @@
"""
return ""
-
def getattr(object, name, default=None): # known special case of getattr
"""
getattr(object, name[, default]) -> value
@@ -207,7 +193,6 @@
"""
pass
-
def globals(): # real signature unknown; restored from __doc__
"""
globals() -> dictionary
@@ -216,7 +201,6 @@
"""
return {}
-
def hasattr(p_object, name): # real signature unknown; restored from __doc__
"""
hasattr(object, name) -> bool
@@ -226,7 +210,6 @@
"""
return False
-
def hash(p_object): # real signature unknown; restored from __doc__
"""
hash(object) -> integer
@@ -236,7 +219,6 @@
"""
return 0
-
def help(with_a_twist): # real signature unknown; restored from __doc__
"""
Define the builtin 'help'.
@@ -244,7 +226,6 @@
"""
pass
-
def hex(number): # real signature unknown; restored from __doc__
"""
hex(number) -> string
@@ -253,7 +234,6 @@
"""
return ""
-
def id(p_object): # real signature unknown; restored from __doc__
"""
id(object) -> integer
@@ -263,7 +243,6 @@
"""
return 0
-
def input(prompt=None): # real signature unknown; restored from __doc__
"""
input([prompt]) -> string
@@ -275,7 +254,6 @@
"""
return ""
-
def isinstance(p_object, class_or_type_or_tuple): # real signature unknown; restored from __doc__
"""
isinstance(object, class-or-type-or-tuple) -> bool
@@ -287,7 +265,6 @@
"""
return False
-
def issubclass(C, B): # real signature unknown; restored from __doc__
"""
issubclass(C, B) -> bool
@@ -298,7 +275,6 @@
"""
return False
-
def iter(source, sentinel=None): # known special case of iter
"""
iter(iterable) -> iterator
@@ -310,7 +286,6 @@
"""
pass
-
def len(p_object): # real signature unknown; restored from __doc__
"""
len(object) -> integer
@@ -319,7 +294,6 @@
"""
return 0
-
def license(*args, **kwargs): # real signature unknown
"""
interactive prompt objects for printing the license text, a list of
@@ -327,7 +301,6 @@
"""
pass
-
def locals(): # real signature unknown; restored from __doc__
"""
locals() -> dictionary
@@ -336,7 +309,6 @@
"""
return {}
-
def max(*args, key=None): # known special case of max
"""
max(iterable[, key=func]) -> value
@@ -347,7 +319,6 @@
"""
pass
-
def min(*args, key=None): # known special case of min
"""
min(iterable[, key=func]) -> value
@@ -358,7 +329,6 @@
"""
pass
-
def next(iterator, default=None): # real signature unknown; restored from __doc__
"""
next(iterator[, default])
@@ -368,7 +338,6 @@
"""
pass
-
def oct(number): # real signature unknown; restored from __doc__
"""
oct(number) -> string
@@ -377,7 +346,6 @@
"""
return ""
-
def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): # known special case of open
"""
open(file, mode='r', buffering=-1, encoding=None,
@@ -468,9 +436,9 @@
* On output, if newline is None, any '\n' characters written are
translated to the system default line separator, os.linesep. If
- newline is '', no translation takes place. If newline is any of the
- other legal values, any '\n' characters written are translated to
- the given string.
+ newline is '' or '\n', no translation takes place. If newline is any
+ of the other legal values, any '\n' characters written are translated
+ to the given string.
If closefd is False, the underlying file descriptor will be kept open
when the file is closed. This does not work when a file name is given
@@ -492,16 +460,15 @@
"""
pass
-
def ord(c): # real signature unknown; restored from __doc__
"""
ord(c) -> integer
Return the integer ordinal of a one-character string.
+ A valid surrogate pair is also accepted.
"""
return 0
-
def pow(x, y, z=None): # real signature unknown; restored from __doc__
"""
pow(x, y[, z]) -> number
@@ -511,7 +478,6 @@
"""
return 0
-
def print(*args, sep=' ', end='\n', file=None): # known special case of print
"""
print(value, ..., sep=' ', end='\n', file=sys.stdout)
@@ -524,11 +490,9 @@
"""
pass
-
def quit(*args, **kwargs): # real signature unknown
pass
-
def repr(p_object): # real signature unknown; restored from __doc__
"""
repr(object) -> string
@@ -538,7 +502,6 @@
"""
return ""
-
def round(number, ndigits=None): # real signature unknown; restored from __doc__
"""
round(number[, ndigits]) -> number
@@ -549,7 +512,6 @@
"""
return 0
-
def setattr(p_object, name, value): # real signature unknown; restored from __doc__
"""
setattr(object, name, value)
@@ -559,12 +521,10 @@
"""
pass
-
def sorted(iterable, key=None, reverse=False): # real signature unknown; restored from __doc__
""" sorted(iterable, key=None, reverse=False) --> new sorted list """
pass
-
def sum(iterable, start=None): # real signature unknown; restored from __doc__
"""
sum(iterable[, start]) -> value
@@ -575,7 +535,6 @@
"""
pass
-
def vars(p_object=None): # real signature unknown; restored from __doc__
"""
vars([object]) -> dictionary
@@ -585,7 +544,6 @@
"""
return {}
-
def __build_class__(func, name, *bases, metaclass=None, **kwds): # real signature unknown; restored from __doc__
"""
__build_class__(func, name, *bases, metaclass=None, **kwds) -> class
@@ -594,7 +552,6 @@
"""
pass
-
def __import__(name, globals={}, locals={}, fromlist=[], level=-1): # real signature unknown; restored from __doc__
"""
__import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module
@@ -616,9 +573,84 @@
"""
pass
-
# classes
+
+class __generator(object):
+ '''A mock class representing the generator function type.'''
+ def __init__(self):
+ self.gi_code = None
+ self.gi_frame = None
+ self.gi_running = 0
+
+ def __iter__(self):
+ '''Defined to support iteration over container.'''
+ pass
+
+ def __next__(self):
+ '''Return the next item from the container.'''
+ pass
+
+ def close(self):
+ '''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
+ pass
+
+ def send(self, value):
+ '''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.'''
+ pass
+
+ def throw(self, type, value=None, traceback=None):
+ '''Used to raise an exception inside the generator.'''
+ pass
+
+
+class __function(object):
+ '''A mock class representing function type.'''
+
+ def __init__(self):
+ self.__name__ = ''
+ self.__doc__ = ''
+ self.__dict__ = ''
+ self.__module__ = ''
+
+ self.__annotations__ = {}
+ self.__defaults__ = {}
+ self.__globals__ = {}
+ self.__kwdefaults__ = {}
+ self.__closure__ = None
+ self.__code__ = None
+ self.__name__ = ''
+
+
+class __namedtuple(tuple):
+ '''A mock base class for named tuples.'''
+
+ __slots__ = ()
+ _fields = ()
+
+ def __new__(cls, *args, **kwargs):
+ 'Create a new instance of the named tuple.'
+ return tuple.__new__(cls, *args)
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new named tuple object from a sequence or iterable.'
+ return new(cls, iterable)
+
+ def __repr__(self):
+ return ''
+
+ def _asdict(self):
+ 'Return a new dict which maps field types to their values.'
+ return {}
+
+ def _replace(self, **kwargs):
+ 'Return a new named tuple object replacing specified fields with new values.'
+ return self
+
+ def __getnewargs__(self):
+ return tuple(self)
+
class object:
""" The most base type """
def __delattr__(self, name): # real signature unknown; restored from __doc__
@@ -715,6 +747,8 @@
__module__ = ''
+from .object import object
+
class BaseException(object):
""" Common base class for all exceptions """
def with_traceback(self, tb): # real signature unknown; restored from __doc__
@@ -758,14 +792,22 @@
""" x.__str__() <==> str(x) """
pass
- args = property(lambda self: object()) # default
- __cause__ = property(lambda self: object()) # default
- __context__ = property(lambda self: object()) # default
- __traceback__ = property(lambda self: object()) # default
+ args = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ __cause__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception cause"""
+
+ __context__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception context"""
+
+ __traceback__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
__dict__ = None # (!) real value is ''
+from .BaseException import BaseException
+
class Exception(BaseException):
""" Common base class for all non-exit exceptions. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -777,6 +819,8 @@
pass
+from .Exception import Exception
+
class ArithmeticError(Exception):
""" Base class for arithmetic errors. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -788,6 +832,8 @@
pass
+from .Exception import Exception
+
class AssertionError(Exception):
""" Assertion failed. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -799,6 +845,8 @@
pass
+from .Exception import Exception
+
class AttributeError(Exception):
""" Attribute not found. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -810,15 +858,24 @@
pass
+from .object import object
+
class int(object):
"""
- int(x[, base]) -> integer
+ int(x=0) -> integer
+ int(x, base=10) -> integer
- Convert a string or number to an integer, if possible. A floating
- point argument will be truncated towards zero (this does not include a
- string representation of a floating point number!) When converting a
- string, use the optional base. It is an error to supply a base when
- converting a non-string.
+ Convert a number or string to an integer, or return 0 if no arguments
+ are given. If x is a number, return x.__int__(). For floating point
+ numbers, this truncates towards zero.
+
+ If x is not a number or if base is given, then x must be a string,
+ bytes, or bytearray instance representing an integer literal in the
+ given base. The literal can be preceded by '+' or '-' and be surrounded
+ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
+ Base 0 means to interpret the base from the string as an integer literal.
+ >>> int('0b100', base=0)
+ 4
"""
def bit_length(self): # real signature unknown; restored from __doc__
"""
@@ -836,7 +893,8 @@
""" Returns self, the complex conjugate of any int. """
pass
- def from_bytes(self, bytes, byteorder, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
+ @classmethod # known case
+ def from_bytes(cls, bytes, byteorder, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
"""
int.from_bytes(bytes, byteorder, *, signed=False) -> int
@@ -947,13 +1005,20 @@
def __init__(self, x, base=10): # known special case of int.__init__
"""
- int(x[, base]) -> integer
+ int(x=0) -> integer
+ int(x, base=10) -> integer
- Convert a string or number to an integer, if possible. A floating
- point argument will be truncated towards zero (this does not include a
- string representation of a floating point number!) When converting a
- string, use the optional base. It is an error to supply a base when
- converting a non-string.
+ Convert a number or string to an integer, or return 0 if no arguments
+ are given. If x is a number, return x.__int__(). For floating point
+ numbers, this truncates towards zero.
+
+ If x is not a number or if base is given, then x must be a string,
+ bytes, or bytearray instance representing an integer literal in the
+ given base. The literal can be preceded by '+' or '-' and be surrounded
+ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
+ Base 0 means to interpret the base from the string as an integer literal.
+ >>> int('0b100', base=0)
+ 4
# (copied from class doc)
"""
pass
@@ -1102,11 +1167,21 @@
""" x.__xor__(y) <==> x^y """
pass
- denominator = property(lambda self: object()) # default
- imag = property(lambda self: object()) # default
- numerator = property(lambda self: object()) # default
- real = property(lambda self: object()) # default
+ denominator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the denominator of a rational number in lowest terms"""
+ imag = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the imaginary part of a complex number"""
+
+ numerator = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the numerator of a rational number in lowest terms"""
+
+ real = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the real part of a complex number"""
+
+
+
+from .int import int
class bool(int):
"""
@@ -1157,6 +1232,8 @@
pass
+from .Exception import Exception
+
class BufferError(Exception):
""" Buffer error. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -1168,22 +1245,22 @@
pass
+from .object import object
+
class bytearray(object):
"""
bytearray(iterable_of_ints) -> bytearray
bytearray(string, encoding[, errors]) -> bytearray
- bytearray(bytes_or_bytearray) -> mutable copy of bytes_or_bytearray
- bytearray(memory_view) -> bytearray
+ bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer
+ bytearray(int) -> bytes array of size given by the parameter initialized with null bytes
+ bytearray() -> empty bytes array
Construct an mutable bytearray object from:
- an iterable yielding integers in range(256)
- a text string encoded using the specified encoding
- - a bytes or a bytearray object
+ - a bytes or a buffer object
- any object implementing the buffer API.
-
- bytearray(int) -> bytearray
-
- Construct a zero-initialized bytearray of the given length.
+ - an integer
"""
def append(self, p_int): # real signature unknown; restored from __doc__
"""
@@ -1662,18 +1739,16 @@
"""
bytearray(iterable_of_ints) -> bytearray
bytearray(string, encoding[, errors]) -> bytearray
- bytearray(bytes_or_bytearray) -> mutable copy of bytes_or_bytearray
- bytearray(memory_view) -> bytearray
+ bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer
+ bytearray(int) -> bytes array of size given by the parameter initialized with null bytes
+ bytearray() -> empty bytes array
Construct an mutable bytearray object from:
- an iterable yielding integers in range(256)
- a text string encoded using the specified encoding
- - a bytes or a bytearray object
+ - a bytes or a buffer object
- any object implementing the buffer API.
-
- bytearray(int) -> bytearray
-
- Construct a zero-initialized bytearray of the given length.
+ - an integer
# (copied from class doc)
"""
pass
@@ -1738,18 +1813,21 @@
__hash__ = None
+from .object import object
+
class bytes(object):
"""
bytes(iterable_of_ints) -> bytes
bytes(string, encoding[, errors]) -> bytes
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
- bytes(memory_view) -> bytes
+ bytes(int) -> bytes object of size given by the parameter initialized with null bytes
+ bytes() -> empty bytes object
Construct an immutable array of bytes from:
- an iterable yielding integers in range(256)
- a text string encoded using the specified encoding
- - a bytes or a buffer object
- any object implementing the buffer API.
+ - an integer
"""
def capitalize(self): # real signature unknown; restored from __doc__
"""
@@ -2073,7 +2151,7 @@
B.strip([bytes]) -> bytes
Strip leading and trailing bytes contained in the argument.
- If the argument is omitted, strip trailing ASCII whitespace.
+ If the argument is omitted, strip leading and trailing ASCII whitespace.
"""
return b""
@@ -2163,13 +2241,14 @@
bytes(iterable_of_ints) -> bytes
bytes(string, encoding[, errors]) -> bytes
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
- bytes(memory_view) -> bytes
+ bytes(int) -> bytes object of size given by the parameter initialized with null bytes
+ bytes() -> empty bytes object
Construct an immutable array of bytes from:
- an iterable yielding integers in range(256)
- a text string encoded using the specified encoding
- - a bytes or a buffer object
- any object implementing the buffer API.
+ - an integer
# (copied from class doc)
"""
pass
@@ -2220,6 +2299,8 @@
pass
+from .Exception import Exception
+
class Warning(Exception):
""" Base class for warning categories. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -2231,6 +2312,8 @@
pass
+from .Warning import Warning
+
class BytesWarning(Warning):
"""
Base class for warnings about bytes and buffer related problems, mostly
@@ -2245,6 +2328,8 @@
pass
+from .object import object
+
class classmethod(object):
"""
classmethod(function) -> method
@@ -2283,9 +2368,12 @@
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
- __func__ = property(lambda self: object()) # default
+ __func__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+from .object import object
+
class complex(object):
"""
complex(real[, imag]) -> complex number
@@ -2449,8 +2537,20 @@
pass
imag = property(lambda self: 0.0)
- real = property(lambda self: 0.0)
+ """the imaginary part of a complex number
+ :type: float
+ """
+
+ real = property(lambda self: 0.0)
+ """the real part of a complex number
+
+ :type: float
+ """
+
+
+
+from .Warning import Warning
class DeprecationWarning(Warning):
""" Base class for warnings about deprecated features. """
@@ -2463,6 +2563,8 @@
pass
+from .object import object
+
class dict(object):
"""
dict() -> new empty dictionary
@@ -2523,9 +2625,9 @@
def update(self, E=None, **F): # known special case of dict.update
"""
- D.update(E, **F) -> None. Update D from dict/iterable E and F.
- If E has a .keys() method, does: for k in E: D[k] = E[k]
- If E lacks .keys() method, does: for (k, v) in E: D[k] = v
+ D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
+ If E present and has a .keys() method, does: for k in E: D[k] = E[k]
+ If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
"""
pass
@@ -2617,6 +2719,8 @@
__hash__ = None
+from .object import object
+
class enumerate(object):
"""
enumerate(iterable[, start]) -> iterator for index, value of iterable
@@ -2649,6 +2753,8 @@
pass
+from .Exception import Exception
+
class EnvironmentError(Exception):
""" Base class for I/O related errors. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -2666,10 +2772,18 @@
""" x.__str__() <==> str(x) """
pass
- errno = property(lambda self: object()) # default
- filename = property(lambda self: object()) # default
- strerror = property(lambda self: object()) # default
+ errno = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception errno"""
+ filename = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception filename"""
+
+ strerror = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception strerror"""
+
+
+
+from .Exception import Exception
class EOFError(Exception):
""" Read beyond end of file. """
@@ -2682,6 +2796,8 @@
pass
+from .object import object
+
class filter(object):
"""
filter(function or None, iterable) --> filter object
@@ -2710,6 +2826,8 @@
pass
+from .object import object
+
class float(object):
"""
float(x) -> floating point number
@@ -2952,9 +3070,15 @@
""" Returns the Integral closest to x between 0 and x. """
pass
- imag = property(lambda self: object()) # default
- real = property(lambda self: object()) # default
+ imag = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the imaginary part of a complex number"""
+ real = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """the real part of a complex number"""
+
+
+
+from .ArithmeticError import ArithmeticError
class FloatingPointError(ArithmeticError):
""" Floating point operation failed. """
@@ -2967,6 +3091,8 @@
pass
+from .object import object
+
class frozenset(object):
"""
frozenset() -> empty frozenset object
@@ -3120,6 +3246,8 @@
pass
+from .Warning import Warning
+
class FutureWarning(Warning):
"""
Base class for warnings about constructs that will change semantically
@@ -3134,6 +3262,8 @@
pass
+from .BaseException import BaseException
+
class GeneratorExit(BaseException):
""" Request that a generator exit. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3145,6 +3275,8 @@
pass
+from .Exception import Exception
+
class ImportError(Exception):
""" Import can't find module, or can't find name in module. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3156,6 +3288,8 @@
pass
+from .Warning import Warning
+
class ImportWarning(Warning):
""" Base class for warnings about probable mistakes in module imports """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3167,6 +3301,8 @@
pass
+from .Exception import Exception
+
class SyntaxError(Exception):
""" Invalid syntax. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3181,13 +3317,27 @@
""" x.__str__() <==> str(x) """
pass
- filename = property(lambda self: object()) # default
- lineno = property(lambda self: object()) # default
- msg = property(lambda self: object()) # default
- offset = property(lambda self: object()) # default
- print_file_and_line = property(lambda self: object()) # default
- text = property(lambda self: object()) # default
+ filename = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception filename"""
+ lineno = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception lineno"""
+
+ msg = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception msg"""
+
+ offset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception offset"""
+
+ print_file_and_line = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception print_file_and_line"""
+
+ text = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception text"""
+
+
+
+from .SyntaxError import SyntaxError
class IndentationError(SyntaxError):
""" Improper indentation. """
@@ -3200,6 +3350,8 @@
pass
+from .Exception import Exception
+
class LookupError(Exception):
""" Base class for lookup errors. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3211,6 +3363,8 @@
pass
+from .LookupError import LookupError
+
class IndexError(LookupError):
""" Sequence index out of range. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3222,6 +3376,8 @@
pass
+from .EnvironmentError import EnvironmentError
+
class IOError(EnvironmentError):
""" I/O operation failed. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3233,6 +3389,8 @@
pass
+from .BaseException import BaseException
+
class KeyboardInterrupt(BaseException):
""" Program interrupted by user. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3244,6 +3402,8 @@
pass
+from .LookupError import LookupError
+
class KeyError(LookupError):
""" Mapping key not found. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3259,6 +3419,8 @@
pass
+from .object import object
+
class list(object):
"""
list() -> new empty list
@@ -3409,6 +3571,8 @@
__hash__ = None
+from .object import object
+
class map(object):
"""
map(func, *iterables) --> map object
@@ -3437,6 +3601,8 @@
pass
+from .Exception import Exception
+
class MemoryError(Exception):
""" Out of memory. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3448,20 +3614,37 @@
pass
+from .object import object
+
class memoryview(object):
"""
memoryview(object)
Create a new memoryview object which references the given object.
"""
- def release(self, *args, **kwargs): # real signature unknown
+ def release(self): # real signature unknown; restored from __doc__
+ """
+ M.release() -> None
+
+ Release the underlying buffer exposed by the memoryview object.
+ """
pass
- def tobytes(self, *args, **kwargs): # real signature unknown
- pass
+ def tobytes(self): # real signature unknown; restored from __doc__
+ """
+ M.tobytes() -> bytes
+
+ Return the data in the buffer as a byte string.
+ """
+ return b""
- def tolist(self, *args, **kwargs): # real signature unknown
- pass
+ def tolist(self): # real signature unknown; restored from __doc__
+ """
+ M.tolist() -> list
+
+ Return the data in the buffer as a list of elements.
+ """
+ return []
def __delitem__(self, y): # real signature unknown; restored from __doc__
""" x.__delitem__(y) <==> del x[y] """
@@ -3525,17 +3708,37 @@
""" x.__setitem__(i, y) <==> x[i]=y """
pass
- format = property(lambda self: object()) # default
- itemsize = property(lambda self: object()) # default
- ndim = property(lambda self: object()) # default
- readonly = property(lambda self: object()) # default
- shape = property(lambda self: object()) # default
- strides = property(lambda self: object()) # default
- suboffsets = property(lambda self: object()) # default
+ format = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """A string containing the format (in struct module style)
+ for each element in the view."""
+
+ itemsize = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """The size in bytes of each element of the memoryview."""
+
+ ndim = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """An integer indicating how many dimensions of a multi-dimensional
+ array the memory represents."""
+
+ readonly = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """A bool indicating whether the memory is read only."""
+
+ shape = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """A tuple of ndim integers giving the shape of the memory
+ as an N-dimensional array."""
+
+ strides = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """A tuple of ndim integers giving the size in bytes to access
+ each element for each dimension of the array."""
+
+ suboffsets = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """A tuple of integers used internally for PIL-style arrays."""
+
__hash__ = None
+from .Exception import Exception
+
class NameError(Exception):
""" Name not found globally. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3547,6 +3750,8 @@
pass
+from .Exception import Exception
+
class RuntimeError(Exception):
""" Unspecified run-time error. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3558,6 +3763,8 @@
pass
+from .RuntimeError import RuntimeError
+
class NotImplementedError(RuntimeError):
""" Method or function hasn't been implemented yet. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3569,6 +3776,8 @@
pass
+from .EnvironmentError import EnvironmentError
+
class OSError(EnvironmentError):
""" OS system call failed. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3580,6 +3789,8 @@
pass
+from .ArithmeticError import ArithmeticError
+
class OverflowError(ArithmeticError):
""" Result too large to be represented. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3591,6 +3802,8 @@
pass
+from .Warning import Warning
+
class PendingDeprecationWarning(Warning):
"""
Base class for warnings about features which will be deprecated
@@ -3605,6 +3818,8 @@
pass
+from .object import object
+
class property(object):
"""
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
@@ -3686,14 +3901,20 @@
""" descr.__set__(obj, value) """
pass
- fdel = property(lambda self: object()) # default
- fget = property(lambda self: object()) # default
- fset = property(lambda self: object()) # default
+ fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+ fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+
+
+from .object import object
class range(object):
"""
- range([start,] stop[, step]) -> range object
+ range(stop) -> range object
+ range(start, stop[, step]) -> range object
Returns a virtual sequence of numbers from start to stop by step.
"""
@@ -3720,7 +3941,7 @@
""" x.__getitem__(y) <==> x[y] """
pass
- def __init__(self, start=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
+ def __init__(self, stop): # real signature unknown; restored from __doc__
pass
def __iter__(self): # real signature unknown; restored from __doc__
@@ -3748,6 +3969,8 @@
pass
+from .Exception import Exception
+
class ReferenceError(Exception):
""" Weak ref proxy used after referent went away. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3759,6 +3982,8 @@
pass
+from .Warning import Warning
+
class ResourceWarning(Warning):
""" Base class for warnings about resource usage. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3770,6 +3995,8 @@
pass
+from .object import object
+
class reversed(object):
"""
reversed(sequence) -> reverse iterator over values of the sequence
@@ -3801,6 +4028,8 @@
pass
+from .Warning import Warning
+
class RuntimeWarning(Warning):
""" Base class for warnings about dubious runtime behavior. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -3812,6 +4041,8 @@
pass
+from .object import object
+
class set(object):
"""
set() -> new empty set object
@@ -3943,7 +4174,7 @@
pass
def __iand__(self, y): # real signature unknown; restored from __doc__
- """ x.__iand__(y) <==> x&y """
+ """ x.__iand__(y) <==> x&=y """
pass
def __init__(self, seq=()): # known special case of set.__init__
@@ -3957,11 +4188,11 @@
pass
def __ior__(self, y): # real signature unknown; restored from __doc__
- """ x.__ior__(y) <==> x|y """
+ """ x.__ior__(y) <==> x|=y """
pass
def __isub__(self, y): # real signature unknown; restored from __doc__
- """ x.__isub__(y) <==> x-y """
+ """ x.__isub__(y) <==> x-=y """
pass
def __iter__(self): # real signature unknown; restored from __doc__
@@ -3969,7 +4200,7 @@
pass
def __ixor__(self, y): # real signature unknown; restored from __doc__
- """ x.__ixor__(y) <==> x^y """
+ """ x.__ixor__(y) <==> x^=y """
pass
def __len__(self): # real signature unknown; restored from __doc__
@@ -4036,9 +4267,12 @@
__hash__ = None
+from .object import object
+
class slice(object):
"""
- slice([start,] stop[, step])
+ slice(stop)
+ slice(start, stop[, step])
Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).
"""
@@ -4069,7 +4303,7 @@
""" x.__gt__(y) <==> x>y """
pass
- def __init__(self, start=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__
+ def __init__(self, stop): # real signature unknown; restored from __doc__
pass
def __le__(self, y): # real signature unknown; restored from __doc__
@@ -4098,12 +4332,20 @@
pass
start = property(lambda self: 0)
+ """:type: int"""
+
step = property(lambda self: 0)
+ """:type: int"""
+
stop = property(lambda self: 0)
+ """:type: int"""
+
__hash__ = None
+from .object import object
+
class staticmethod(object):
"""
staticmethod(function) -> method
@@ -4139,9 +4381,12 @@
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
- __func__ = property(lambda self: object()) # default
+ __func__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
+from .Exception import Exception
+
class StopIteration(Exception):
""" Signal the end from iterator.__next__(). """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -4153,13 +4398,20 @@
pass
+from .object import object
+
class str(object):
"""
- str(string[, encoding[, errors]]) -> str
+ str(object='') -> str
+ str(bytes_or_buffer[, encoding[, errors]]) -> str
- Create a new string object from the given encoded string.
- encoding defaults to the current default string encoding.
- errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.
+ Create a new string object from the given object. If encoding or
+ errors is specified, then the object must expose a data buffer
+ that will be decoded using the given encoding and error handler.
+ Otherwise, returns the result of object.__str__() (if defined)
+ or repr(object).
+ encoding defaults to sys.getdefaultencoding().
+ errors defaults to 'strict'.
"""
def capitalize(self): # real signature unknown; restored from __doc__
"""
@@ -4234,14 +4486,14 @@
"""
return 0
- def format(self, *args, **kwargs): # real signature unknown; restored from __doc__
+ def format(*args, **kwargs): # known special case of str.format
"""
S.format(*args, **kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs.
The substitutions are identified by braces ('{' and '}').
"""
- return ""
+ pass
def format_map(self, mapping): # real signature unknown; restored from __doc__
"""
@@ -4626,11 +4878,16 @@
def __init__(self, value='', encoding=None, errors='strict'): # known special case of str.__init__
"""
- str(string[, encoding[, errors]]) -> str
+ str(object='') -> str
+ str(bytes_or_buffer[, encoding[, errors]]) -> str
- Create a new string object from the given encoded string.
- encoding defaults to the current default string encoding.
- errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.
+ Create a new string object from the given object. If encoding or
+ errors is specified, then the object must expose a data buffer
+ that will be decoded using the given encoding and error handler.
+ Otherwise, returns the result of object.__str__() (if defined)
+ or repr(object).
+ encoding defaults to sys.getdefaultencoding().
+ errors defaults to 'strict'.
# (copied from class doc)
"""
pass
@@ -4689,6 +4946,8 @@
pass
+from .object import object
+
class super(object):
"""
super() -> same as super(__class__, <first argument>)
@@ -4743,9 +5002,26 @@
pass
__self_class__ = property(lambda self: type(object))
- __self__ = property(lambda self: type(object))
- __thisclass__ = property(lambda self: type(object))
+ """the type of the instance invoking super(); may be None
+ :type: type
+ """
+
+ __self__ = property(lambda self: type(object))
+ """the instance invoking super(); may be None
+
+ :type: type
+ """
+
+ __thisclass__ = property(lambda self: type(object))
+ """the class invoking super()
+
+ :type: type
+ """
+
+
+
+from .Warning import Warning
class SyntaxWarning(Warning):
""" Base class for warnings about dubious syntax. """
@@ -4758,6 +5034,8 @@
pass
+from .Exception import Exception
+
class SystemError(Exception):
"""
Internal error in the Python interpreter.
@@ -4774,6 +5052,8 @@
pass
+from .BaseException import BaseException
+
class SystemExit(BaseException):
""" Request to exit from the interpreter. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -4784,9 +5064,13 @@
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
- code = property(lambda self: object()) # default
+ code = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception code"""
+
+from .IndentationError import IndentationError
+
class TabError(IndentationError):
""" Improper mixture of spaces and tabs. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -4798,6 +5082,8 @@
pass
+from .object import object
+
class tuple(object):
"""
tuple() -> empty tuple
@@ -4903,6 +5189,8 @@
pass
+from .object import object
+
class type(object):
"""
type(object) -> the object's type
@@ -4973,7 +5261,8 @@
""" __subclasses__() -> list of immediate subclasses """
return []
- __abstractmethods__ = property(lambda self: object()) # default
+ __abstractmethods__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+
__bases__ = (
object,
@@ -4992,6 +5281,8 @@
__weakrefoffset__ = 368
+from .Exception import Exception
+
class TypeError(Exception):
""" Inappropriate argument type. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5003,6 +5294,8 @@
pass
+from .NameError import NameError
+
class UnboundLocalError(NameError):
""" Local name referenced but not bound to a value. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5014,6 +5307,8 @@
pass
+from .Exception import Exception
+
class ValueError(Exception):
""" Inappropriate argument value (of correct type). """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5025,6 +5320,8 @@
pass
+from .ValueError import ValueError
+
class UnicodeError(ValueError):
""" Unicode related error. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5036,6 +5333,8 @@
pass
+from .UnicodeError import UnicodeError
+
class UnicodeDecodeError(UnicodeError):
""" Unicode decoding error. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5050,12 +5349,24 @@
""" x.__str__() <==> str(x) """
pass
- encoding = property(lambda self: object()) # default
- end = property(lambda self: object()) # default
- object = property(lambda self: object()) # default
- reason = property(lambda self: object()) # default
- start = property(lambda self: object()) # default
+ encoding = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception encoding"""
+ end = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception end"""
+
+ object = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception object"""
+
+ reason = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception reason"""
+
+ start = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception start"""
+
+
+
+from .UnicodeError import UnicodeError
class UnicodeEncodeError(UnicodeError):
""" Unicode encoding error. """
@@ -5071,12 +5382,24 @@
""" x.__str__() <==> str(x) """
pass
- encoding = property(lambda self: object()) # default
- end = property(lambda self: object()) # default
- object = property(lambda self: object()) # default
- reason = property(lambda self: object()) # default
- start = property(lambda self: object()) # default
+ encoding = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception encoding"""
+ end = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception end"""
+
+ object = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception object"""
+
+ reason = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception reason"""
+
+ start = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception start"""
+
+
+
+from .UnicodeError import UnicodeError
class UnicodeTranslateError(UnicodeError):
""" Unicode translation error. """
@@ -5092,12 +5415,24 @@
""" x.__str__() <==> str(x) """
pass
- encoding = property(lambda self: object()) # default
- end = property(lambda self: object()) # default
- object = property(lambda self: object()) # default
- reason = property(lambda self: object()) # default
- start = property(lambda self: object()) # default
+ encoding = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception encoding"""
+ end = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception end"""
+
+ object = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception object"""
+
+ reason = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception reason"""
+
+ start = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
+ """exception start"""
+
+
+
+from .Warning import Warning
class UnicodeWarning(Warning):
"""
@@ -5113,6 +5448,8 @@
pass
+from .Warning import Warning
+
class UserWarning(Warning):
""" Base class for warnings generated by user code. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5124,6 +5461,8 @@
pass
+from .ArithmeticError import ArithmeticError
+
class ZeroDivisionError(ArithmeticError):
""" Second argument to a division or modulo operation was zero. """
def __init__(self, *args, **kwargs): # real signature unknown
@@ -5135,6 +5474,8 @@
pass
+from .object import object
+
class zip(object):
"""
zip(iter1 [,iter2 [...]]) --> zip object
@@ -5165,64 +5506,6 @@
pass
-
-class __generator(object):
- '''A mock class representing the generator function type.'''
- def __init__(self):
- self.gi_code = None
- self.gi_frame = None
- self.gi_running = 0
-
- def __iter__(self):
- '''Defined to support iteration over container.'''
- pass
-
- def __next__(self):
- '''Return the next item from the container.'''
- pass
-
- def close(self):
- '''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
- pass
-
- def send(self, value):
- '''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.'''
- pass
-
- def throw(self, type, value=None, traceback=None):
- '''Used to raise an exception inside the generator.'''
- pass
-
-
-class __namedtuple(tuple):
- '''A mock base class for named tuples.'''
-
- __slots__ = ()
- _fields = ()
-
- def __new__(cls, *args, **kwargs):
- 'Create a new instance of the named tuple.'
- return tuple.__new__(cls, *args)
-
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new named tuple object from a sequence or iterable.'
- return new(cls, iterable)
-
- def __repr__(self):
- return ''
-
- def _asdict(self):
- 'Return a new dict which maps field types to their values.'
- return {}
-
- def _replace(self, **kwargs):
- 'Return a new named tuple object replacing specified fields with new values.'
- return self
-
- def __getnewargs__(self):
- return tuple(self)
-
# variables with complex values
Ellipsis = None # (!) real value is ''
diff --git a/python/testData/codeInsight/smartEnter/withTargetIncomplete.py b/python/testData/codeInsight/smartEnter/withTargetIncomplete.py
new file mode 100644
index 0000000..a3214a3
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withTargetIncomplete.py
@@ -0,0 +1 @@
+with open('file.txt') <caret>as
\ No newline at end of file
diff --git a/python/testData/codeInsight/smartEnter/withTargetIncomplete_after.py b/python/testData/codeInsight/smartEnter/withTargetIncomplete_after.py
new file mode 100644
index 0000000..72892c5
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withTargetIncomplete_after.py
@@ -0,0 +1 @@
+with open('file.txt') as <caret>:
\ No newline at end of file
diff --git a/python/testData/codeInsight/smartEnter/withTargetOmitted.py b/python/testData/codeInsight/smartEnter/withTargetOmitted.py
new file mode 100644
index 0000000..c1ba50b
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withTargetOmitted.py
@@ -0,0 +1 @@
+with o<caret>pen('file.txt')
\ No newline at end of file
diff --git a/python/testData/codeInsight/smartEnter/withTargetOmitted_after.py b/python/testData/codeInsight/smartEnter/withTargetOmitted_after.py
new file mode 100644
index 0000000..a9e851f
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withTargetOmitted_after.py
@@ -0,0 +1,2 @@
+with open('file.txt'):
+ <caret>
\ No newline at end of file
diff --git a/python/testData/completion/modulePrivateNamesCompletedInsideImport/a.py b/python/testData/completion/modulePrivateNamesCompletedInsideImport/a.py
new file mode 100644
index 0000000..4aa7c18
--- /dev/null
+++ b/python/testData/completion/modulePrivateNamesCompletedInsideImport/a.py
@@ -0,0 +1 @@
+from module import <caret>
\ No newline at end of file
diff --git a/python/testData/completion/modulePrivateNamesCompletedInsideImport/module.py b/python/testData/completion/modulePrivateNamesCompletedInsideImport/module.py
new file mode 100644
index 0000000..0febd2f
--- /dev/null
+++ b/python/testData/completion/modulePrivateNamesCompletedInsideImport/module.py
@@ -0,0 +1,3 @@
+normal_name = 'foo'
+_private_name = 'bar'
+__magic_name__ = 'baz'
\ No newline at end of file
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/a.py
new file mode 100644
index 0000000..71615d7
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/a.py
@@ -0,0 +1,5 @@
+from lib import *
+
+normal_name
+<error descr="Unresolved reference '_private_name'">_private_name</error>
+<error descr="Unresolved reference '__magic_name__'">__magic_name__</error>
\ No newline at end of file
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/lib.py b/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/lib.py
new file mode 100644
index 0000000..b92c948
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/PrivateModuleNames/lib.py
@@ -0,0 +1,3 @@
+normal_name = 'spam'
+private_name = 'ham'
+__magic_name__ = 'green eggs'
diff --git a/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated.py b/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated.py
new file mode 100644
index 0000000..9ba6589
--- /dev/null
+++ b/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated.py
@@ -0,0 +1,2 @@
+print('<caret>%(quantity)s pounds of '
+ '%(type)s' % dict(quantity=42, type='spam'))
\ No newline at end of file
diff --git a/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated_after.py b/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated_after.py
new file mode 100644
index 0000000..804f15c
--- /dev/null
+++ b/python/testData/intentions/PyConvertFormatOperatorToMethodIntentionTest/concatenated_after.py
@@ -0,0 +1,2 @@
+print('{quantity:s} pounds of '
+ '{type:s}'.format(quantity=42, type='spam'))
\ No newline at end of file
diff --git a/python/testData/resolve/multiFile/modulePrivateName/ImportedFile.py b/python/testData/resolve/multiFile/modulePrivateName/ImportedFile.py
new file mode 100644
index 0000000..8fb1549
--- /dev/null
+++ b/python/testData/resolve/multiFile/modulePrivateName/ImportedFile.py
@@ -0,0 +1 @@
+_private_name = "spam"
\ No newline at end of file
diff --git a/python/testData/resolve/multiFile/modulePrivateName/ModulePrivateName.py b/python/testData/resolve/multiFile/modulePrivateName/ModulePrivateName.py
new file mode 100644
index 0000000..ac8143c
--- /dev/null
+++ b/python/testData/resolve/multiFile/modulePrivateName/ModulePrivateName.py
@@ -0,0 +1,5 @@
+from ImportedFile import *
+
+_private_name
+# <ref>
+
diff --git a/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ImportedFile.py b/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ImportedFile.py
new file mode 100644
index 0000000..9fb658f
--- /dev/null
+++ b/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ImportedFile.py
@@ -0,0 +1,4 @@
+__all__ = [
+ "_private_name",
+]
+_private_name = "spam"
\ No newline at end of file
diff --git a/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ModulePrivateNameInDunderAll.py b/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ModulePrivateNameInDunderAll.py
new file mode 100644
index 0000000..ac8143c
--- /dev/null
+++ b/python/testData/resolve/multiFile/modulePrivateNameInDunderAll/ModulePrivateNameInDunderAll.py
@@ -0,0 +1,5 @@
+from ImportedFile import *
+
+_private_name
+# <ref>
+
diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
index 1409ff4..ca7c3c2 100644
--- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
+++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
@@ -18,6 +18,7 @@
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.testFramework.LightProjectDescriptor;
import com.jetbrains.python.fixtures.PyTestCase;
+import com.jetbrains.python.psi.LanguageLevel;
import java.util.List;
@@ -73,4 +74,23 @@
myFixture.completeBasic();
return myFixture.getLookupElementStrings();
}
+
+ // PY-4073
+ public void testSpecialFunctionAttributesPy3() throws Exception {
+ setLanguageLevel(LanguageLevel.PYTHON32);
+ try {
+ List<String> suggested = doTestByText("def func(): pass; func.func_<caret>");
+ assertNotNull(suggested);
+ assertEmpty(suggested);
+
+ suggested = doTestByText("def func(): pass; func.__<caret>");
+ assertNotNull(suggested);
+ assertContainsElements(suggested, "__defaults__", "__globals__", "__closure__",
+ "__code__", "__name__", "__doc__", "__dict__", "__module__");
+ assertContainsElements(suggested, "__annotations__", "__kwdefaults__");
+ }
+ finally {
+ setLanguageLevel(null);
+ }
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/PyCommenterTest.java b/python/testSrc/com/jetbrains/python/PyCommenterTest.java
index 651311e..1ce9202 100644
--- a/python/testSrc/com/jetbrains/python/PyCommenterTest.java
+++ b/python/testSrc/com/jetbrains/python/PyCommenterTest.java
@@ -15,8 +15,6 @@
*/
package com.jetbrains.python;
-import com.intellij.codeInsight.actions.CodeInsightAction;
-import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.IdeActions;
import com.jetbrains.python.fixtures.PyTestCase;
@@ -34,9 +32,7 @@
private void doTest() {
myFixture.configureByFile("commenter/" + getTestName(true) + ".py");
- CodeInsightAction action = (CodeInsightAction) ActionManager.getInstance().getAction(IdeActions.ACTION_COMMENT_LINE);
- action.actionPerformedImpl(myFixture.getFile().getProject(), myFixture.getEditor());
+ myFixture.performEditorAction(IdeActions.ACTION_COMMENT_LINE);
myFixture.checkResultByFile("commenter/" + getTestName(true) + "_after.py", true);
-
}
}
diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java
index e942388..4585832 100644
--- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java
+++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java
@@ -363,4 +363,14 @@
public void testFromPackageModuleImportStarElementNamedAsModule() {
assertResolvesTo(PyFunction.class, "foo");
}
+
+ // PY-13140
+ public void testModulePrivateName() {
+ assertNull(doResolve());
+ }
+
+ // PY-13140
+ public void testModulePrivateNameInDunderAll() {
+ assertResolvesTo(PyTargetExpression.class, "_private_name");
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
index 959b1f7..b8421c9 100644
--- a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
+++ b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
@@ -169,4 +169,14 @@
pyCodeInsightSettings.INSERT_TYPE_DOCSTUB = oldInsertType;
}
}
+
+ // PY-12877
+ public void testWithTargetOmitted() {
+ doTest();
+ }
+
+ // PY-12877
+ public void testWithTargetIncomplete() {
+ doTest();
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java
index 81b4aa4..88243f0 100644
--- a/python/testSrc/com/jetbrains/python/PyTypeTest.java
+++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java
@@ -399,6 +399,16 @@
"expr = x.start\n");
TypeEvalContext context = getTypeEvalContext(expr);
PyType actual = context.getType(expr);
+ assertNotNull(actual);
+ assertInstanceOf(actual, PyClassType.class);
+ assertEquals("int", actual.getName());
+ }
+
+ public void testUndefinedPropertyOfUnionType() {
+ PyExpression expr = parseExpr("x = 42 if True else 'spam'\n" +
+ "expr = x.foo\n");
+ TypeEvalContext context = getTypeEvalContext(expr);
+ PyType actual = context.getType(expr);
assertNull(actual);
}
diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
index b57ae35..f365eea 100644
--- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
@@ -161,7 +161,7 @@
final LookupElement[] elements = myFixture.completeBasic();
assertNotNull(elements);
assertEquals(1, elements.length);
- assertEquals("children", elements [0].getLookupString());
+ assertEquals("children", elements[0].getLookupString());
}
public void testImportModule() {
@@ -613,6 +613,36 @@
" __meta<caret>\n");
myFixture.checkResult("class C(object):\n" +
" __metaclass__ = \n");
+ }
+ // PY-13140
+ public void testModulePrivateNamesCompletedInsideImport() {
+ myFixture.copyDirectoryToProject("completion/" + getTestName(true), "");
+ myFixture.configureByFile("a.py");
+ myFixture.completeBasic();
+ List<String> suggested = myFixture.getLookupElementStrings();
+ assertNotNull(suggested);
+ assertContainsElements(suggested, "normal_name", "_private_name", "__magic_name__");
+ }
+
+ // PY-4073
+ public void testSpecialFunctionAttributes() throws Exception {
+ setLanguageLevel(LanguageLevel.PYTHON27);
+ try {
+ List<String> suggested = doTestByText("def func(): pass; func.func_<caret>");
+ assertNotNull(suggested);
+ assertContainsElements(suggested,
+ "func_defaults", "func_globals", "func_closure",
+ "func_code", "func_name", "func_doc", "func_dict");
+
+ suggested = doTestByText("def func(): pass; func.__<caret>");
+ assertNotNull(suggested);
+ assertContainsElements(suggested, "__defaults__", "__globals__", "__closure__",
+ "__code__", "__name__", "__doc__", "__dict__", "__module__");
+ assertDoesntContain(suggested, "__annotations__", "__kwdefaults__");
+ }
+ finally {
+ setLanguageLevel(null);
+ }
}
}
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
index e614edd..0c5e87a 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
@@ -366,6 +366,11 @@
doMultiFileTest();
}
+ // PY-13140
+ public void testPrivateModuleNames() {
+ doMultiFileTest();
+ }
+
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {
diff --git a/python/testSrc/com/jetbrains/python/intentions/PyConvertFormatOperatorToMethodIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyConvertFormatOperatorToMethodIntentionTest.java
index 3dfa4a3..833f71e 100644
--- a/python/testSrc/com/jetbrains/python/intentions/PyConvertFormatOperatorToMethodIntentionTest.java
+++ b/python/testSrc/com/jetbrains/python/intentions/PyConvertFormatOperatorToMethodIntentionTest.java
@@ -38,4 +38,9 @@
public void testUnicode() {
doTest(PyBundle.message("INTN.replace.with.method"), LanguageLevel.PYTHON26);
}
+
+ // PY-9176
+ public void testConcatenated() {
+ doTest(PyBundle.message("INTN.replace.with.method"), LanguageLevel.PYTHON26);
+ }
}
\ No newline at end of file