From 3b92a2936da74763f8c39d3c53702f9226d24999 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 8 Apr 2021 14:47:26 +0900 Subject: [PATCH 1/1] Imported Upstream version python3-empy 3.3.2 --- COPYING | 504 +++ README | 2313 ++++++++++++++ doc/home/max/projects/empy/doc/em.html | 506 +++ .../max/projects/empy/doc/em/AbstractFile.html | 305 ++ doc/home/max/projects/empy/doc/em/BreakFlow.html | 88 + .../max/projects/empy/doc/em/BufferedFilter.html | 190 ++ .../max/projects/empy/doc/em/CommentToken.html | 192 ++ doc/home/max/projects/empy/doc/em/Context.html | 193 ++ .../max/projects/empy/doc/em/ContextLineToken.html | 197 ++ .../max/projects/empy/doc/em/ContextNameToken.html | 196 ++ .../max/projects/empy/doc/em/ContinueFlow.html | 88 + .../max/projects/empy/doc/em/ControlToken.html | 457 +++ doc/home/max/projects/empy/doc/em/CustomToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Diversion.html | 275 ++ .../max/projects/empy/doc/em/DiversionError.html | 83 + doc/home/max/projects/empy/doc/em/Document.html | 108 + doc/home/max/projects/empy/doc/em/Error.html | 78 + doc/home/max/projects/empy/doc/em/EscapeToken.html | 228 ++ .../max/projects/empy/doc/em/ExpansionToken.html | 195 ++ .../max/projects/empy/doc/em/ExpressionToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Filter.html | 431 +++ doc/home/max/projects/empy/doc/em/FilterError.html | 83 + doc/home/max/projects/empy/doc/em/FlowError.html | 83 + .../max/projects/empy/doc/em/FunctionFilter.html | 160 + doc/home/max/projects/empy/doc/em/Hook.html | 1655 ++++++++++ doc/home/max/projects/empy/doc/em/HookError.html | 83 + .../max/projects/empy/doc/em/InPlaceToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Interpreter.html | 3167 +++++++++++++++++++ .../projects/empy/doc/em/LineBufferedFilter.html | 165 + .../max/projects/empy/doc/em/LiteralToken.html | 168 + .../empy/doc/em/MaximallyBufferedFilter.html | 193 ++ doc/home/max/projects/empy/doc/em/MetaError.html | 164 + doc/home/max/projects/empy/doc/em/NullFile.html | 216 ++ doc/home/max/projects/empy/doc/em/NullFilter.html | 131 + doc/home/max/projects/empy/doc/em/NullToken.html | 191 ++ doc/home/max/projects/empy/doc/em/ParseError.html | 83 + doc/home/max/projects/empy/doc/em/PrefixToken.html | 168 + doc/home/max/projects/empy/doc/em/Processor.html | 323 ++ doc/home/max/projects/empy/doc/em/ProxyFile.html | 369 +++ doc/home/max/projects/empy/doc/em/ReprToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Scanner.html | 1267 ++++++++ .../projects/empy/doc/em/SignificatorToken.html | 226 ++ .../empy/doc/em/SimpleExpressionToken.html | 196 ++ .../projects/empy/doc/em/SizeBufferedFilter.html | 165 + doc/home/max/projects/empy/doc/em/Stack.html | 455 +++ .../projects/empy/doc/em/StackUnderflowError.html | 83 + .../max/projects/empy/doc/em/StatementToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Stream.html | 753 +++++ .../max/projects/empy/doc/em/StringFilter.html | 188 ++ .../projects/empy/doc/em/StringLiteralToken.html | 196 ++ doc/home/max/projects/empy/doc/em/Subsystem.html | 318 ++ .../max/projects/empy/doc/em/SubsystemError.html | 83 + doc/home/max/projects/empy/doc/em/Token.html | 219 ++ .../projects/empy/doc/em/TransientParseError.html | 89 + .../max/projects/empy/doc/em/UncloseableFile.html | 217 ++ doc/home/max/projects/empy/doc/em/VerboseHook.html | 133 + .../max/projects/empy/doc/em/WhitespaceToken.html | 136 + doc/index.html | 2247 +++++++++++++ em.py | 3302 ++++++++++++++++++++ sample.bench | 164 + sample.em | 336 ++ setup.py | 35 + test.sh | 29 + version.txt | 1 + 64 files changed, 25847 insertions(+) create mode 100644 COPYING create mode 100644 README create mode 100644 doc/home/max/projects/empy/doc/em.html create mode 100644 doc/home/max/projects/empy/doc/em/AbstractFile.html create mode 100644 doc/home/max/projects/empy/doc/em/BreakFlow.html create mode 100644 doc/home/max/projects/empy/doc/em/BufferedFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/CommentToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Context.html create mode 100644 doc/home/max/projects/empy/doc/em/ContextLineToken.html create mode 100644 doc/home/max/projects/empy/doc/em/ContextNameToken.html create mode 100644 doc/home/max/projects/empy/doc/em/ContinueFlow.html create mode 100644 doc/home/max/projects/empy/doc/em/ControlToken.html create mode 100644 doc/home/max/projects/empy/doc/em/CustomToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Diversion.html create mode 100644 doc/home/max/projects/empy/doc/em/DiversionError.html create mode 100644 doc/home/max/projects/empy/doc/em/Document.html create mode 100644 doc/home/max/projects/empy/doc/em/Error.html create mode 100644 doc/home/max/projects/empy/doc/em/EscapeToken.html create mode 100644 doc/home/max/projects/empy/doc/em/ExpansionToken.html create mode 100644 doc/home/max/projects/empy/doc/em/ExpressionToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Filter.html create mode 100644 doc/home/max/projects/empy/doc/em/FilterError.html create mode 100644 doc/home/max/projects/empy/doc/em/FlowError.html create mode 100644 doc/home/max/projects/empy/doc/em/FunctionFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/Hook.html create mode 100644 doc/home/max/projects/empy/doc/em/HookError.html create mode 100644 doc/home/max/projects/empy/doc/em/InPlaceToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Interpreter.html create mode 100644 doc/home/max/projects/empy/doc/em/LineBufferedFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/LiteralToken.html create mode 100644 doc/home/max/projects/empy/doc/em/MaximallyBufferedFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/MetaError.html create mode 100644 doc/home/max/projects/empy/doc/em/NullFile.html create mode 100644 doc/home/max/projects/empy/doc/em/NullFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/NullToken.html create mode 100644 doc/home/max/projects/empy/doc/em/ParseError.html create mode 100644 doc/home/max/projects/empy/doc/em/PrefixToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Processor.html create mode 100644 doc/home/max/projects/empy/doc/em/ProxyFile.html create mode 100644 doc/home/max/projects/empy/doc/em/ReprToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Scanner.html create mode 100644 doc/home/max/projects/empy/doc/em/SignificatorToken.html create mode 100644 doc/home/max/projects/empy/doc/em/SimpleExpressionToken.html create mode 100644 doc/home/max/projects/empy/doc/em/SizeBufferedFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/Stack.html create mode 100644 doc/home/max/projects/empy/doc/em/StackUnderflowError.html create mode 100644 doc/home/max/projects/empy/doc/em/StatementToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Stream.html create mode 100644 doc/home/max/projects/empy/doc/em/StringFilter.html create mode 100644 doc/home/max/projects/empy/doc/em/StringLiteralToken.html create mode 100644 doc/home/max/projects/empy/doc/em/Subsystem.html create mode 100644 doc/home/max/projects/empy/doc/em/SubsystemError.html create mode 100644 doc/home/max/projects/empy/doc/em/Token.html create mode 100644 doc/home/max/projects/empy/doc/em/TransientParseError.html create mode 100644 doc/home/max/projects/empy/doc/em/UncloseableFile.html create mode 100644 doc/home/max/projects/empy/doc/em/VerboseHook.html create mode 100644 doc/home/max/projects/empy/doc/em/WhitespaceToken.html create mode 100644 doc/index.html create mode 100755 em.py create mode 100644 sample.bench create mode 100644 sample.em create mode 100644 setup.py create mode 100755 test.sh create mode 100644 version.txt diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/README b/README new file mode 100644 index 0000000..d9368f5 --- /dev/null +++ b/README @@ -0,0 +1,2313 @@ +Summary + + A powerful and robust templating system for Python. + + +Overview + + EmPy is a system for embedding Python expressions and statements + in template text; it takes an EmPy source file, processes it, and + produces output. This is accomplished via expansions, which are + special signals to the EmPy system and are set off by a special + prefix (by default the at sign, '@'). EmPy can expand arbitrary + Python expressions and statements in this way, as well as a + variety of special forms. Textual data not explicitly delimited + in this way is sent unaffected to the output, allowing Python to + be used in effect as a markup language. Also supported are + callbacks via hooks, recording and playback via diversions, and + dynamic, chainable filters. The system is highly configurable via + command line options and embedded commands. + + Expressions are embedded in text with the '@(...)' notation; + variations include conditional expressions with '@(...?...!...)' + and the ability to handle thrown exceptions with '@(...$...)'. As + a shortcut, simple variables and expressions can be abbreviated as + '@variable', '@object.attribute', '@function(arguments)', + '@sequence' [index], and combinations. Full-fledged statements + are embedded with '@{...}'. Control flow in terms of conditional + or repeated expansion is available with '@[...]'. A '@' followed + by a whitespace character (including a newline) expands to + nothing, allowing string concatenations and line continuations. + Comments are indicated with '@#' and consume the rest of the line, + up to and including the trailing newline. '@%' indicate + "significators," which are special forms of variable assignment + intended to specify per-file identification information in a + format which is easy to parse externally. Context name and line + number changes can be done with '@?' and '@!' respectively. + '@<...>' markups are customizeable by the user and can be used for + any desired purpose. Escape sequences analogous to those in C can + be specified with '@\...', and finally a '@@' sequence expands to + a single literal at sign. + + +Getting the software + + The current version of empy is 3.3.2. + + The latest version of the software is available in a tarball here: + "http://www.alcyone.com/software/empy/empy-latest.tar.gz", + http://www.alcyone.com/software/empy/empy-latest.tar.gz. + + The official URL for this Web site is + "http://www.alcyone.com/software/empy/", + http://www.alcyone.com/software/empy/. + + +Requirements + + EmPy should work with any version of Python from 2.4 onward, + including 3.x. + + +License + + This code is released under the "LGPL", + http://www.gnu.org/copyleft/lesser.html. + + +Mailing lists + + There are two EmPy related mailing lists available. The first is + a receive-only, very low volume list for important announcements + (including releases). To subscribe, send an email to + "empy-announce-list-subscribe@alcyone.com", + mailto:empy-announce-list-subscribe@alcyone.com. + + The second is a general discussion list for topics related to + EmPy, and is open for everyone to contribute; announcements + related to EmPy will also be made on this list. The author of + EmPy (and any future developers) will also be on the list, so it + can be used not only to discuss EmPy features with other users, + but also to ask questions of the author(s). To subscribe, send an + email to "empy-list-subscribe@alcyone.com", + mailto:empy-list-subscribe@alcyone.com. + + +Basics + + EmPy is intended for embedding Python code in otherwise + unprocessed text. Source files are processed, and the results are + written to an output file. Normal text is sent to the output + unchanged, but markups are processed, expanded to their results, + and then written to the output file as strings (that is, with the + 'str' function, not 'repr'). The act of processing EmPy source + and handling markups is called "expansion." + + Code that is processed is executed exactly as if it were entered + into the Python interpreter; that is, it is executed with the + equivalent of 'eval' (for expressions) and 'exec' (for + statements). EmPy is intended to be a very thin (though powerful) + layer on top of a running Python system; Python and EmPy files can + be mixed together (via command line options) without + complications. + + By default the embedding prefix is the at sign ('@'), which + appears neither in valid Python code nor commonly in arbitrary + texts; it can be overridden with the -p option (or with the + 'empy.setPrefix' function). The prefix indicates to the EmPy + interpreter that a special sequence follows and should be + processed rather than sent to the output untouched (to indicate a + literal at sign, it can be doubled as in '@@'). + + When the interpreter starts processing its target file, no modules + are imported by default, save the 'empy' pseudomodule (see below), + which is placed in the globals; the 'empy' pseudomodule is + associated with a particular interpreter -- in fact, they are the + same object -- and it is important that it not be removed from + that interpreter's globals, nor that it be shared with other + interpreters running concurrently (a name other than 'empy' can be + specified with the -m option). The globals are not cleared or + reset in any way. It is perfectly legal to set variables or + explicitly import modules and then use them in later markups, + *e.g.*, '@{import time} ... @time.time()'. Scoping rules are as + in normal Python, although all defined variables and objects are + taken to be in the global namespace. + + Indentation is significant in Python, and therefore is also + significant in EmPy. EmPy statement markups ('@{...}'), when + spanning multiple lines, must be flush with the left margin. This + is because (multiline) statement markups are not treated specially + in EmPy and are simply passed to the Python interpreter, where + indentation is significant. + + Activities you would like to be done before any processing of the + main EmPy file can be specified with the -I, -D, -E, -F, and -P + options. -I imports modules, -D executes a Python variable + assignment, -E executes an arbitrary Python (not EmPy) statement, + -F executes a Python (not EmPy) file, and -P processes an EmPy + (not Python) file. These operations are done in the order they + appear on the command line; any number of each (including, of + course, zero) can be used. + + +Expansions + + The following markups are supported. For concreteness below, '@' + is taken for the sake of argument to be the prefix character, + although this can be changed. + + **'@# COMMENT NEWLINE'** -- A comment. Comments, including the + trailing newline, are stripped out completely. Comments should + only be present outside of expansions. The comment itself is + not processed in any way: It is completely discarded. This + allows '@#' comments to be used to disable markups. *Note:* As + special support for "bangpaths" in Unix-like operating systems, + if the first line of a file (or indeed any context) begins with + '#!', and the interpreter has a 'processBangpaths' option set to + true (default), it is treated as a '@#' comment. A '#!' + sequence appearing anywhere else will be handled literally and + unaltered in the expansion. Example:: + + @# This line is a comment. + @# This will NOT be expanded: @x. + + **'@? NAME NEWLINE'** -- Set the name of the current context to be + the given string. Variables are not allowed here; the name is + treated as a literal. (If you wish to use arbitrary + expressions, use the 'empy.setContextName' function instead.) + Example:: + + @?NewName + The context name is now @empy.identify()[0] (NewName). + + **'@! INTEGER NEWLINE'** -- Set the line number of the current + context to be the given integer value; this is similar to the + '#line' C preprocessor directive. This is done in such a way + that the *next* line will have the specified numeric value, not + the current one. Expressions are not allowed here; the number + must be a literal integer. (If you wish to use arbitrary + expressions, use the 'empy.setContextLine' function instead.) + Example:: + + @!100 + The context line is now @empy.identify()[1] (100). + + **'@ WHITESPACE'** -- A '@' followed by one whitespace character + (a space, horizontal tab, vertical tab, carriage return, or + newline) is expanded to nothing; it serves as a way to + explicitly separate two elements which might otherwise be + interpreted as being the same symbol (such as '@name@ s' to mean + '@(name)s' -- see below). Also, since a newline qualifies as + whitespace here, the lone '@' at the end of a line represents a + line continuation, similar to the backslash in other languages. + Coupled with statement expansion below, spurious newlines can be + eliminated in statement expansions by use of the '@{...}@' + construct. Example:: + + This will appear as one word: salt@ water. + This is a line continuation; @ + this text will appear on the same line. + + **'@\ ESCAPE_CODE'** -- An escape code. Escape codes in EmPy are + similar to C-style escape codes, although they all begin with + the prefix character. Valid escape codes include: + + '@\0' -- NUL, null + + '@\a' -- BEL, bell + + '@\b' -- BS, backspace + + '@\d' -- three-digital decimal code DDD + + '@\e' -- ESC, escape + + '@\f' -- FF, form feed + + '@\h' -- DEL, delete + + '@\n' -- LF, linefeed character, newline + + '@\oOOO' -- three-digit octal code OOO + + '@\qQQQQ' -- four-digit quaternary code QQQQ + + '@\r' -- CR, carriage return + + '@\s' -- SP, space + + '@\t' -- HT, horizontal tab + + '@\v' -- VT, vertical tab + + '@\xHH' -- two-digit hexadecimal code HH + + '@\z' -- EOT, end of transmission + + '@^X' -- the control character ^X + + Unlike in C-style escape codes, escape codes taking some number + of digits afterward always take the same number to prevent + ambiguities. Furthermore, unknown escape codes are treated as + parse errors to discourage potential subtle mistakes. Note + that, while '@\0' represents the NUL character, to represent an + octal code, one must use '@\o...', in contrast to C. Example:: + + This embeds a newline.@\nThis is on the following line. + This beeps!@\a + There is a tab here:@\tSee? + This is the character with octal code 141: @\o141. + + **'@@'** -- A literal at sign ('@'). To embed two adjacent at + signs, use '@@@@', and so on. Any literal at sign that you wish + to appear in your text must be written this way, so that it will + not be processed by the system. *Note:* If a prefix other than + '@' has been chosen via the command line option, one expresses + that literal prefix by doubling it, not by appending a '@'. + Example:: + + The prefix character is @@. + To get the expansion of x you would write @@x. + + **'@)', '@]', '@}'** -- These expand to literal close parentheses, + close brackets, and close braces, respectively; these are + included for completeness and explicitness only. Example:: + + This is a close parenthesis: @). + + **'@"..."', '@"""..."""', etc.** -- These string literals expand + to the literals themselves, so '@"test"' expands to 'test'. + Since they are inherently no-operations, the only reason for + their use is to override their behavior with hooks. + + **'@( EXPRESSION )'** -- Evaluate an expression, and expand with + the string (via a call to 'str') representation evaluation of + that expression. Whitespace immediately inside the parentheses + is ignored; '@( expression )' is equivalent to '@(expression)'. + If the expression evaluates to 'None', nothing is expanded in + its place; this allows function calls that depend on side + effects (such as printing) to be called as expressions. (If you + really *do* want a 'None' to appear in the output, then use the + Python string '"None"'.) *Note:* If an expression prints + something to 'sys.stdout' as a side effect, then that printing + will be spooled to the output *before* the expression's return + value is. Example:: + + 2 + 2 is @(2 + 2). + 4 squared is @(4**2). + The value of the variable x is @(x). + This will be blank: @(None). + + **'@( TEST ? THEN (! ELSE)_opt ($ EXCEPT)_opt )'** -- A special + form of expression evaluation representing conditional and + protected evaluation. Evaluate the "test" expression; if it + evaluates to true (in the Pythonic sense), then evaluate the + "then" section as an expression and expand with the 'str' of + that result. If false, then the "else" section is evaluated and + similarly expanded. The "else" section is optional and, if + omitted, is equivalent to 'None' (that is, no expansion will + take place). *Note*: For backward compatibility, the "else" + section delimiter, '!', may be expressed as a ':'. This + behavior is supported but deprecated. + + If the "except" section is present, then if any of the prior + expressions raises an exception when evaluated, the expansion + will be replaced with the evaluation of the except expression. + (If the "except" expression itself raises, then that exception + will be propagated normally.) The except section is optional + and, if omitted, is equivalent to 'None' (that is, no expansion + will take place). An exception (cough) to this is if one of + these first expressions raises a SyntaxError; in that case the + protected evaluation lets the error through without evaluating + the "except" expression. The intent of this construct is to + except runtime errors, and if there is actually a syntax error + in the "try" code, that is a problem that should probably be + diagnosed rather than hidden. Example:: + + What is x? x is @(x ? "true" ! "false"). + Pluralization: How many words? @x word@(x != 1 ? 's'). + The value of foo is @(foo $ "undefined"). + Division by zero is @(x/0 $ "illegal"). + + **'@ SIMPLE_EXPRESSION'** -- As a shortcut for the '@(...)' + notation, the parentheses can be omitted if it is followed by a + "simple expression." A simple expression consists of a name + followed by a series of function applications, array + subscriptions, or attribute resolutions, with no intervening + whitespace. For example: + + - a name, possibly with qualifying attributes (*e.g.*, + '@value', '@os.environ'). + + - a straightforward function call (*e.g.*, '@min(2, 3)', + '@time.ctime()'), with no space between the function name + and the open parenthesis. + + - an array subscription (*e.g.*, '@array[index]', + '@os.environ[name]', with no space between the name and + the open bracket. + + - any combination of the above (*e.g.*, + '@function(args).attr[sub].other[i](foo)'). + + In essence, simple expressions are expressions that can be + written ambiguously from text, without intervening space. Note + that trailing dots are not considered part of the expansion + (*e.g.*, '@x.' is equivalent to '@(x).', not '@(x.)', which + would be illegal anyway). Also, whitespace is allowed within + parentheses or brackets since it is unambiguous, but not between + identifiers and parentheses, brackets, or dots. Explicit + '@(...)' notation can be used instead of the abbreviation when + concatenation is what one really wants (*e.g.*, '@(word)s' for + simple pluralization of the contents of the variable 'word'). + As above, if the expression evaluates to the 'None' object, + nothing is expanded. Note that since a curly appearing where + EmPy would expect an open parenthesis or bracket in is + meaningless in Python, it is treated as a parse error (*e.g.*, + '@x{1, 2}' results in an error). Example:: + + The value of x is @x. + The ith value of a is @a[i]. + The result of calling f with q is @f(q). + The attribute a of x is @x.a. + The current time is @time.ctime(time.time()). + The current year is @time.localtime(time.time())[0]. + These are the same: @min(2,3) and @min(2, 3). + But these are not the same: @min(2, 3) vs. @min (2, 3). + The plural of @name is @(name)s, or @name@ s. + + **'@` EXPRESSION `'** -- Evaluate a expression, and expand with + the 'repr' (instead of the 'str' which is the default) of the + evaluation of that expression. This expansion is primarily + intended for debugging and is unlikely to be useful in actual + practice. That is, a '@`...`' is identical to '@(repr(...))'. + Example:: + + The repr of the value of x is @`x`. + This print the Python repr of a module: @`time`. + This actually does print None: @`None`. + + **'@: EXPRESSION : DUMMY :'** -- Evaluate an expression and then + expand to a '@:', the original expression, a ':', the evaluation + of the expression, and then a ':'. The current contents of the + dummy area are ignored in the new expansion. In this sense it + is self-evaluating; the syntax is available for use in + situations where the same text will be sent through the EmPy + processor multiple times. Example:: + + This construct allows self-evaluation: + @:2 + 2:this will get replaced with 4: + + **'@{ STATEMENTS }'** -- Execute a (potentially compound) + statement; statements have no return value, so the expansion is + not replaced with anything. Multiple statements can either be + separated on different lines, or with semicolons; indentation is + significant, just as in normal Python code. Statements, + however, can have side effects, including printing; output to + 'sys.stdout' (explicitly or via a 'print' statement) is + collected by the interpreter and sent to the output (unless this + behavior is suppressed with the -n option). The usual Python + indentation rules must be followed, although if the statement + consists of only one statement, leading and trailing whitespace + is ignored (*e.g.*, '@{ print time.time() }' is equivalent to + '@{print time.time()}'). Example:: + + @{x = 123} + @{a = 1; b = 2} + @{print time.time()} + @# Note that extra newlines will appear above because of the + @# newlines trailing the close braces. To suppress them + @# use a @ before the newline: + @{ + for i in range(10): + print "i is %d" % i + }@ + @{print "Welcome to EmPy."}@ + + **'@% KEY (WHITESPACE VALUE)_opt NEWLINE'** -- Declare a + significator. Significators consume the whole line (including + the trailing newline), and consist of a key string containing no + whitespace, and than optional value prefixed by whitespace. The + key may not start with or contain internal whitespace, but the + value may; preceding or following whitespace in the value is + stripped. Significators are totally optional, and are intended + to be used for easy external (that is, outside of EmPy) + identification when used in large scale environments with many + EmPy files to be processed. The purpose of significators is to + provide identification information about each file in a special, + easy-to-parse form so that external programs can process the + significators and build databases, independently of EmPy. + Inside of EmPy, when a significator is encountered, its key, + value pair is translated into a simple assignment of the form + '__KEY__ = VALUE' , where "__KEY__" is the key string with two + underscores on either side and "VALUE" is a Python expression. + Example:: + + @%title "Gravitation" + @%author "Misner", "Thorne", "Wheeler" + @%publisher "W.H. Freeman and Company" + @%pages 1279 + @%keywords 'physics', 'gravity', 'Einstein', 'relativity' + @%copyright 1970, 1971 + + **'@< CONTENTS >'** -- Invoke a custom markup. The custom markup + is a special markup reserved for use by the user; it has no + prescribed meaning on its own. If 'contents' is a string + representing what appears in between the angle brackets, then + expanding this markup is equivalent to + 'empy.invokeCallback(contents)'. See the "Custom markup" + section for more information. + + +Control + + EmPy version 3 and above includes the ability to direct + conditional and repeated expansion of blocks of EmPy code with + control markups (the obsolescent "substitution" markups are + unavailable as of version 3.0). Control markups have analogs to + control flow structures in Python such as 'if/elif/else', 'for', and + 'while'. Control markups are set off with the '@[...]' notation. + + Control markups are designed to be used in precisely the same way + that their internal Python analogues are used, except that the + control markups are intended to be used where there is much more + markup than control structure. + + Some control markups are considered "primary," (*e.g.*, 'if', + 'for', 'while') as they begin a control markup. Others are + considered "secondary," since they can only appear inside control + flow markups delineated by primary markups (*e.g.*, 'elif', + 'else', 'continue', 'break'). + + Since EmPy, unlike Python, cannot use indentation to determine + where control structures begin and end, all primary control + markups *must* be followed by a corresponding terminating control + markup:: + + @[PRIMARY ...]...@[end PRIMARY] + + (where 'PRIMARY' represents one of the primary keywords). The end + markup is mandatory, as is the space between the 'end' and the + starting keyword. For instance:: + + @# If `person' is alive, show their age. + @person.name is @ + @[if person.isAlive]@person.age@[else]dead@[end if]. + + All primary markups must be terminated in this way, and the + keyword appearing in the appropriate 'end' markup must match the + primary markup it corresponds to; if either of these conditions + are not satisfied, the result is a parse error. Everything + between the starting control flow marker ('@[PRIMARY ...]') and + the ending marker ('@[end PRIMARY]') -- including other markups, + even control markups -- is considered part of the markup. Control + markups can be nested:: + + @# Print all non-false elements on separate lines. + @[for elem in elements]@[if elem]@elem@\n@[end if]@[end for] + + Three major types of primary control markups are available: + conditional (*e.g.*, 'if', 'try'), looping (*e.g.*, 'for', + 'while'), and definitional (*e.g.*, 'def', discussed below). + Conditional control markups conditionally expand their contents, + whereas looping control markups repeatedly expand their contents. + The third type, definitional markups, will define new objects in + the globals relating to their contents. Conditional and looping + markups also differ in one substantial respect: Looping constructs + support '@[continue]' and '@[break]' markups which, like their + Python equivalents, continue with the next iteration or break out + of the innermost looping construct, respectively ('@[continue]' + and '@[break]' markups have no meaning inside conditional markups + and are an error). Also like their Python equivalents, + '@[continue]' and '@[break]' may appear inside nested markups, so + long as they ultimately are contained by at least one looping + control markup:: + + @# Walk a long a linked list, printing each element. + @[while 1]@ + @node + @{node = node.next}@ + @[if not node]@[break]@[end if]@ + @[end while] + + The provided markups are designed to mimic the internal Python + control structures as closely as possible. The supported control + markups are (the phrases in all uppercase are intended to signify + user-selectable patterns):: + + @[if CONDITION1]...@[elif CONDITION2]...@[else]...@[end if] + @[try]...@[except ...]...@[except ...]...@[end try] + @[try]...@[finally]...@[end try] + @[for VARIABLE in SEQUENCE]...@[else]...@[end for] + @[while CONDITION]...@[else]...@[end while] + @[def SIGNATURE]...@[end def] + + All recognizable forms behave like their Python equivalents; 'if' + can contain multiple 'elif' secondary markups within it; the + 'else' markups are optional (but must appear at the end), the + 'try' form with the 'except' clause can contain multiple ones + which are handled in sequence, the 'try' form can either contain + one or more 'except' clauses or one 'finally' clause (but not + both), and the 'for' and 'while' structures can contain 'continue' + or 'break' clauses internally (even if contained within other + markups). + + The third type of primary control markup is "definitional," in + that they create objects in the globals for later use (*e.g.*, + 'def'). This allows the definition of a callable object which, + when called, will expand the contained markup (which can in turn, + of course, contain further markups). The argument to the markup + can be any legal Python function signature:: + + @[def f(x, y, z=2, *args, **keywords)]...@[end def] + + would define a function in the globals named 'f' that takes the + given arguments. A macro markup of the form '@[def + SIGNATURE]CODE@[end def]' is equivalent to the Python code:: + + def SIGNATURE: + r"""CODE""" # so it is a doc string + empy.expand(r"""CODE""", locals()) + + That is, it creates a Python function with the same name and + function arguments, whose docstring is the contents of the EmPy + markup that will be expanded when called. And, when called, it + will expand those contents, with the locals passed in. + + +Unicode support + + EmPy version 3.1 and above includes intrinsic Unicode support. + EmPy's Unicode support defers to Python's internal Unicode + support, available in Python 2.0 and up, in order to allow + seamless and transparent translation of different encodings to the + native Python Unicode format. + + Knowledge of Python's Unicode support is expected, although not + completely required, to gain full benefit of EmPy's Unicode + features. To enable Unicode support, start EmPy with the + -u/--unicode option. EmPy will then transparently encode from the + input stream, process markups internally with native Unicode, and + then decode transparently to the output stream. + + By default, Python sets 'sys.stdin' and 'sys.stdout' with a + default encoding which is accessible via + 'sys.getdefaultencoding()'; encodings are represented by string + names. These streams have encodings set by the system and + *cannot* be changed. + + However, encodings for newly created files (files to be read when + specified on the command line, and/or files to be written when + used with the -o and -a arguments) can be specified for EmPy via + command line options. The --unicode-encoding option + simultaneously indicates the encoding to be used for both input + and output, whereas the --unicode-input-encoding and + --unicode-output-encoding options can each be used to specify + different encodings for both input and output. (If an encoding is + not explicitly indicated, it resorts to the system default in + 'sys.getdefaultencoding()', which is locale dependent.) + + Python's Unicode implementation has the concept of error handlers, + registered with the 'codecs' module, which can be specified to + determine what action should take place if input cannot be decoded + into Unicode, or Unicode cannot be encoded into output. EmPy uses + these same "errors," as they are called, and can be specified via + command line options. The three most common error handlers are: + 'ignore', where invalid sequences are simply ignored; 'replace', + where invalid sequences are replaced with an encoding-specific + indicator, usually a question mark; and 'strict', where invalid + sequences raise an error. The --unicode-errors command line + option specifies the same error handler to be used for both input + and output, and the --unicode-input-errors and + --unicode-output-errors options can specify different error + handlers for input and output. If an error handler is not + explicitly specified, the 'strict' handler (which will raise + errors) is used. + + Remember, to specify input encodings or errors that will take + effect, one cannot take input from 'sys.stdin' and must explicitly + specify an EmPy file to process on the command line. Similarly, + for output encodings or errors, 'sys.stdout' cannot be used and an + explicit output file must be specified with the -o or -a options. + It is perfectly valid to enable the Unicode subsystem (-u option) + while using 'sys.stdin' and 'sys.stdout', but the encodings and + errors of these preexisting streams cannot be changed. + + Combined with the --no-prefix option, which disables all markup + processing, EmPy can act merely as an encoding translator, relying + on Python's Unicode facilities:: + + em.py --no-prefix \ + --unicode-input-encoding=utf-8 \ + --unicode-output-encoding=latin-1 \ + -o filename.Latin-1 filename.UTF-8 + + +Significators + + Significators, introduced in EmPy version 1.2, are intended to + represent special assignment in a form that is easy to externally + parse. For instance, if one has a system that contains many EmPy + files, each of which has its own title, one could use a 'title' + significator in each file and use a simple regular expression to + find this significator in each file and organize a database of the + EmPy files to be built. This is an easier proposition than, for + instance, attempting to grep for a normal Python assignment + (inside a '@{...}' expansion) of the desired variable. + + Significators look like the following:: + + @%KEY VALUE + + including the trailing newline, where "key" is a name and "value" + is a Python expression, and are separated by any whitespace. This + is equivalent to the following Python code:: + + __KEY__ = VALUE + + That is to say, a significator key translates to a Python variable + consisting of that key surrounded by double underscores on either + side. The value may contain spaces, but the key may not. So:: + + @%title "All Roads Lead to Rome" + + translates to the Python code:: + + __title__ = "All Roads Lead to Rome" + + but obviously in a way that easier to detect externally than if + this Python code were to appear somewhere in an expansion. Since + significator keys are surrounded by double underscores, + significator keys can be any sequence of alphanumeric and + underscore characters; choosing '123' is perfectly valid for a + significator (although straight), since it maps to the name + '__123__' which is a legal Python identifier. + + Note the value can be any Python expression. The value can be + omitted; if missing, it is treated as 'None'. + + Significators are completely optional; it is completely legal for + a EmPy file or files to be processed without containing any + significators. Significators can appear anywhere within a file + outside of other markups, but typically they are placed near the + top of the file to make them easy to spot and edit by humans. + + A regular expression string designed to match significators (with + the default prefix) is available as 'empy.SIGNIFICATOR_RE_STRING', + and also is a toplevel definition in the 'em' module itself. + + + +Diversions + + EmPy supports an extended form of diversions, which are a + mechanism for deferring and recalling output on demand, similar to + the functionality included in m4. Multiple "streams" of output + can be diverted (deferred) and undiverted (recalled) in this + manner. A diversion is identified with a name, which is any + immutable object such an integer or string. When recalled, + diverted code is *not* resent through the EmPy interpreter + (although a filter could be set up to do this). + + By default, no diversions take place. When no diversion is in + effect, processing output goes directly to the specified output + file. This state can be explicitly requested at any time by + calling the 'empy.stopDiverting' function. It is always legal to + call this function. + + When diverted, however, output goes to a deferred location which + can then be recalled later. Output is diverted with the + 'empy.startDiversion' function, which takes an argument that is + the name of the diversion. If there is no diversion by that name, + a new diversion is created and output will be sent to that + diversion; if the diversion already exists, output will be + appended to that preexisting diversion. + + Output send to diversions can be recalled in two ways. The first + is through the 'empy.playDiversion' function, which takes the + name of the diversion as an argument. This recalls the named + diversion, sends it to the output, and then erases that + diversion. A variant of this behavior is the + 'empy.replayDiversion', which recalls the named diversion but does + not eliminate it afterwards; 'empy.replayDiversion' can be + repeatedly called with the same diversion name, and will replay + that diversion repeatedly. 'empy.createDiversion' create a + diversion without actually diverting to it, for cases where you + want to make sure a diversion exists but do not yet want to send + anything to it. + + The diversion object itself can be retrieved with + 'empy.retrieveDiversion'. Diversions act as writable + file-objects, supporting the usual 'write', 'writelines', 'flush', + and 'close' methods. The data that has been diverted to them can + be retrieved in one of two ways; either through the 'asString' + method, which returns the entire contents of the diversion as a + single strong, or through the 'asFile' method, which returns the + contents of the diversion as a readable (not writable) file-like + object. + + Diversions can also be explicitly deleted without recalling them + with the 'empy.purgeDiversion' function, which takes the desired + diversion name as an argument. + + Additionally there are three functions which will apply the above + operations to all existing diversions: 'empy.playAllDiversions', + 'empy.replayAllDiversions', and 'empy.purgeAllDiversions'. All + three will do the equivalent of a 'empy.stopDiverting' call before + they do their thing. + + The name of the current diversion can be requested with the + 'empy.getCurrentDiversion' function; also, the names of all + existing diversions (in sorted order) can be retrieved with + 'empy.getAllDiversions'. + + When all processing is finished, the equivalent of a call to + 'empy.playAllDiversions' is done. + + +Filters + + EmPy also supports dynamic filters, introduced in version 1.3. + Filters are put in place right "before" the final output file, and + so are only invoked after all other processing has taken place + (including interpreting and diverting). Filters take input, remap + it, and then send it to the output. + + The current filter can be retrieved with the 'empy.getFilter' + function. The filter can be cleared (reset to no filter) with + 'empy.resetFilter' and a special "null filter" which does not send + any output at all can be installed with 'empy.nullFilter'. A + custom filter can be set with the 'empy.setFilter' function; for + convenience, specialized shortcuts for filters preexist and can be + used in lieu of actual 'empy.Filter' instances for the + 'empy.setFilter' or 'empy.attachFilter' argument: + + - 'None' is a special filter meaning "no filter"; when installed, + no filtering whatsoever will take place. 'empy.setFilter(None)' + is equivalent to 'empy.resetFilter()'. + + - '0' (or any other numeric constant equal to zero) is another + special filter that represents the null filter; when installed, + no output will ever be sent to the filter's sink. + + - A filter specified as a function (or lambda) is expected to take + one string argument and return one string argument; this filter + will execute the function on any input and use the return value + as output. + + - A filter that is a string is a 256-character table is + substituted with the result of a call to 'string.translate' + using that table. + + - A filter can be an instance of a subclass of 'empy.Filter'. + This is the most general form of filter. (In actuality, it can + be any object that exhibits a 'Filter' interface, which would + include the normal file-like 'write', 'flush', and 'close' + methods, as well as 'next', 'attach', and 'detach' methods for + filter-specific behavior.) + + - Finally, the argument to 'empy.setFilter' can be a Python list + consisting of one or more of the above objects. In that case, + those filters are chained together in the order they appear in + the list. An empty list is the equivalent of 'None'; all + filters will be uninstalled. + + Filters are, at their core, simply file-like objects (minimally + supporting 'write', 'flush', and 'close' methods that behave in + the usual way) which, after performing whatever processing they + need to do, send their work to the next file-like object or filter + in line, called that filter's "sink." That is to say, filters can + be "chained" together; the action of each filter takes place in + sequence, with the output of one filter being the input of the + next. Additionally, filters support a '_flush' method (note the + leading underscore) which will always flush the filter's + underlying sink; this method should be not overridden. + + Filters also support three additional methods, not part of the + traditional file interface: 'attach', which takes as an argument a + file-like object (perhaps another filter) and sets that as the + filter's "sink" -- that is, the next filter/file-like object in + line. 'detach' (which takes no arguments) is another method which + flushes the filter and removes its sink, leaving it isolated. + Finally, 'next' is an accessor method which returns the filter's + sink -- or 'None', if the filter does not yet have a sink + attached. + + To create your own filter, you can create an object which supports + the above described interface, or simply derive from the + 'empy.Filter' class and override its 'write' and possibly 'flush' + methods. You can chain filters together by passing them as + elements in a list to the 'empy.setFilter' function, or you can + chain them together manually with the 'attach' method:: + + firstFilter.attach(secondFilter) + empy.setFilter(firstFilter) + + or just let EmPy do the chaining for you:: + + empy.setFilter([firstFilter, secondFilter]) + + In either case, EmPy will walk the filter chain and find the end + and then hook that into the appropriate interpreter stream; you + need not do this manually. The function 'empy.attachFilter' can + be used to attach a single filter (or shortcut, as above) to the + end of a currently existing chain. Note that unlike its cousin + 'empy.setFilter', one cannot pass a sequence of filters (or filter + shortcuts) to 'empy.attachFilter'. (If there is no existing + filter chain installed, 'empy.attachFilter' will behave the same + as 'empy.setFilter'.) + + Subclasses of 'empy.Filter' are already provided with the above + null, function, and string functionality described above; they are + 'NullFilter', 'FunctionFilter', and 'StringFilter', respectively. + In addition, a filter which supports buffering, 'BufferedFilter', + is provided. Several variants are included: 'SizeBufferedFilter', + a filter which buffers into fixed-sized chunks, + 'LineBufferedFilter', a filter which buffers by lines, and + 'MaximallyBufferedFilter', a filter which completely buffers its + input. + + +Hooks + + The EmPy system allows for the registry of hooks with a running + EmPy interpreter. Originally introduced in version 2.0 and much + improved in 3.2, hooks are objects, registered with an + interpreter, whose methods represent specific callbacks. Any + number of hook objects can be registered with an interpreter, and + when a callback is invoked, the associated method on each one of + those hook objects will be called by the interpreter in sequence. + + Hooks are simply instances, nominally derived from the 'empy.Hook' + class. The 'empy.Hook' class itself defines a series of methods, + with the expected arguments, which would be called by a running + EmPy interpreter. This scenario, much improved from the prior + implementation in 2.0, allows hooks to keep state and have more + direct access to the interpreter they are running in (the + 'empy.Hook' instance contains an 'interpreter' attribute). + + To use a hook, derive a class from 'empy.Hook' and override the + desired methods (with the same signatures as they appear in the + base class). Create an instance of that subclass, and then + register it with a running interpreter with the 'empy.addHook' + function. (This same hook instance can be removed with the + 'empy.removeHook' function.) + + More than one hook instance can be registered with an interpreter; + in such a case, the appropriate methods are invoked on each + instance in the order in which they were registered. To adjust + this behavior, an optional 'prepend' argument to the + 'empy.addHook' function can be used dictate that the new hook + should placed at the *beginning* of the sequence of hooks, rather + than at the end (which is the default). + + All hooks can be enabled and disabled entirely for a given + interpreter; this is done with the 'empy.enableHooks' and + 'empy.disableHooks' functions. By default hooks are enabled, but + obviously if no hooks have been registered no hook callbacks will + be made. Whether hooks are enabled or disabled can be determined + by calling 'empy.areHooksEnabled'. To get a (copy of) the list of + registered hooks, call 'empy.getHooks'. Finally, to invoke a hook + manually, use 'empy.invokeHook'. + + For a list of supported hook callbacks, see the 'empy.Hook' class + definition. + + As a practical example, this sample Python code would print a + pound sign followed by the name of every file that is included + with 'empy.include':: + + class IncludeHook(empy.Hook): + def beforeInclude(self, name, file, locals): + print "# %s" % name + + empy.addHook(IncludeHook()) + + +Custom markup + + Since version 3.2.1, the markup '@<...>' is reserved for + user-defined use. Unlike the other markups, this markup has no + specified meaning on its own, and can be provided a meaning by the + user. This meaning is provided with the use of a "custom + callback," or just "callback," which can be set, queried, or reset + using the pseudomodule function. + + The custom callback is a callable object which, when invoked, is + passed a single argument: a string representing the contents of + what was found inside the custom markup '@<...>'. + + To register a callback, call 'empy.registerCallback'. To remove + one, call 'empy.deregisterCallback'. To retrieve the callback (if + any) registered with the interpreter, use 'empy.getCallback'. + Finally, to invoke the callback just as if the custom markup were + encountered, call 'empy.invokeCallback'. For instance, '@' would be equivalent to the call '@empy.invokeCallback("This + text")'. + + By default, to invoke a callback (either explicitly with + 'empy.invokeCallback' or by processing a '@<...>' custom markup) + when no callback has been registered is an error. This behavior + can be changed with the 'CALLBACK_OPT' option, or the + --no-callback-error command line option. + + +Pseudomodule + + The 'empy' pseudomodule is available only in an operating EmPy + system. (The name of the module, by default 'empy', can be + changed with the -m option or the 'EMPY_PSEUDO' environment + variable). It is called a pseudomodule because it is not actually + a module, but rather exports a module-like interface. In fact, + the pseudmodule is actually the same internal object as the + interpreter itself. + + The pseudomodule contains the following functions and objects (and + their signatures, with a suffixed 'opt' indicating an optional + argument): + + First, basic identification: + + **'VERSION'** -- A constant variable which contains a + string representation of the EmPy version. + + **'SIGNIFICATOR_RE_STRING'** -- A constant variable representing a + regular expression string (using the default prefix) that can be + used to find significators in EmPy code. + + **'SIGNIFICATOR_RE_SUFFIX'** -- The portion of the significator + regular expression string excluding the prefix, so that those + using non-standard prefix can build their own custom regular + expression string with 'myPrefix + empy.SIGNIFICATOR_RE_SUFFIX'. + + **'interpreter'** -- The instance of the interpreter that is + currently being used to perform execution. *Note:* This is now + obsolete; the pseudomodule is itself the interpreter. Instead + of using 'empy.interpreter', simply use 'empy'. + + **'argv'** -- A list consisting of the name of the primary EmPy + script and its command line arguments, in analogue to the + 'sys.argv' list. + + **'args'** -- A list of the command line arguments following the + primary EmPy script; this is equivalent to 'empy.argv[1:]'. + + **'identify() -> string, integer'** -- Retrieve identification + information about the current parsing context. Returns a + 2-tuple consisting of a filename and a line number; if the file + is something other than from a physical file (*e.g.*, an + explicit expansion with 'empy.expand', a file-like object within + Python, or via the -E or -F command line options), a string + representation is presented surrounded by angle brackets. Note + that the context only applies to the *EmPy* context, not the + Python context. + + **'atExit(callable)'** -- Register a callable object (such as a + function) taking no arguments which will be called at the end of + a normal shutdown. Callable objects registered in this way are + called in the reverse order in which they are added, so the + first callable registered with 'empy.atExit' is the last one to + be called. Note that although the functionality is related to + hooks, 'empy.atExit' does no work via the hook mechanism, and + you are guaranteed that the interpreter and stdout will be in a + consistent state when the callable is invoked. + + Context manipulation: + + **'pushContext(name_opt, line_opt)'** -- Create a new context with + the given name and line and push it on the stack. + + **'popContext()'** -- Pop the top context and dispose of it. + + **'setContextName(name)'** -- Manually set the name of the current + context. + + **'setContextLine(line)'** -- Manually set the line number of the + current context; line must be a numeric value. Note that + afterward the line number will increment by one for each newline + that is encountered, as before. + + Globals manipulation: + + **'getGlobals()'** -- Retrieve the globals dictionary for this + interpreter. Unlike when calling 'globals()' in Python, this + dictionary *can* be manipulated and you *can* expect changes you + make to it to be reflected in the interpreter that holds it. + + **'setGlobals(globals)'** -- Reseat the globals dictionary + associated with this interpreter to the provided mapping type. + + **'updateGlobals(globals)'** -- Merge the given dictionary into + this interpreter's globals. + + **'clearGlobals(globals_opt)'** -- Clear out the globals + (restoring, of course, the 'empy' pseudomodule). Optionally, + instead of starting with a refresh dictionary, use the + dictionary provided. + + **'saveGlobals(deep=True)'** -- Save a copy of the globals onto an + internal history stack from which it can be restored later. The + optional 'deep' argument indicates whether or not the copying + should be a deep copy (default) or a shallow one. Copying is + done with 'copy.deepcopy' or 'copy.copy', respectively. + + **'restoreGlobals(destructive=True)'** -- Restore the most + recently saved globals from the history stack to as the current + globals for this instance. The optional 'destructive' argument + indicates whether or not the restore should remove the restored + globals from the history stack (default), or whether it should + be left there for subsequent restores. + + Types: + + **'Interpreter'** -- The actual interpreter class. + + The following functions allow direct execution; optional 'locals' + arguments, if specified, are treated as the locals dictionary in + evaluation and execution: + + **'defined(name, locals_opt)'** -- Return true if the given name + is defined either in the (optional) locals or the interpreter + globals; return false otherwise. + + **'evaluate(expression, locals_opt)'** -- Evaluate the given + expression. + + **'serialize(expression, locals_opt)'** -- Serialize the + expression, just as the interpreter would: If it is not None, + convert it to a string with the 'str' builtin function, and then + write out the result. If it evaluates to None, do nothing. + + **'execute(statements, locals_opt)'** -- Execute the given + statement(s). + + **'single(source, locals_opt)'** -- Interpret the "single" source + code, just as the Python interactive interpreter would. + + **'import_(name, locals_opt)'** -- Import a module. + + **'atomic(name, value, locals_opt)'** -- Perform a single, atomic + assignment. In this case name is the string denoating the name + of the (single) variable to be assigned to, and value is a + Python object which the name is to be bound to. + + **'assign(name, value, locals_opt)'** -- Perform general + assignment. This decays to atomic assignment (above) in the + normal case, but supports "tuple unpacking" in the sense that if + name string contains commas, it is treated as a sequence of + names and memberwise assignment with each member of the value + (still a Python object, but which must be a sequence). This + function will raise a 'TypeError' or 'ValueError' just like + Python would if tuple unpacking is not possible (that is, if the + value is not a sequence or is of an incompatible length, + respectively). This only supports the assignment of Python + identifiers, not arbitrary Python lvalues. + + **'significate(key, value_opt, locals_opt)'** -- Do a manual + signification. If 'value' is not specified, it is treated as + 'None'. + + The following functions relate to source manipulation: + + **'include(file_or_filename, locals_opt)'** -- Include another + EmPy file, by processing it in place. The argument can either + be a filename (which is then opened with 'open' in text mode) or + a file object, which is used as is. Once the included file is + processed, processing of the current file continues. Includes + can be nested. The call also takes an optional locals + dictionary which will be passed into the evaluation function. + + **'expand(string, locals_opt)' -> string** -- Explicitly invoke + the EmPy parsing system to process the given string and return + its expansion. This allows multiple levels of expansion, + *e.g.*, '@(empy.expand("@(2 + 2)"))'. The call also takes an + optional locals dictionary which will be passed into the + evaluation function. This is necessary when text is being + expanded inside a function definition and it is desired that the + function arguments (or just plain local variables) are available + to be referenced within the expansion. + + **'quote(string) -> string'** -- The inverse process of + 'empy.expand', this will take a string and return a new string + that, when expanded, would expand to the original string. In + practice, this means that appearances of the prefix character + are doubled, except when they appear inside a string literal. + + **'escape(string, more_opt) -> string'** -- Given a string, quote + the nonprintable characters contained within it with EmPy + escapes. The optional 'more' argument specifies additional + characters that should be escaped. + + **'flush()'** -- Do an explicit flush on the underlying stream. + + **'string(string, name_opt, locals_opt)'** -- Explicitly process a + string-like object. This differs from 'empy.expand' in that the + string is directly processed into the EmPy system, rather than + being evaluated in an isolated context and then returned as a + string. + + Changing the behavior of the pseudomodule itself: + + **'flatten(keys_opt)'** -- Perform the equivalent of 'from empy + import ...' in code (which is not directly possible because + 'empy' is a pseudomodule). If keys is omitted, it is taken as + being everything in the 'empy' pseudomodule. Each of the + elements of this pseudomodule is flattened into the globals + namespace; after a call to 'empy.flatten', they can be referred + to simple as globals, *e.g.*, '@divert(3)' instead of + '@empy.divert(3)'. If any preexisting variables are bound to + these names, they are silently overridden. Doing this is + tantamount to declaring an 'from ... import ...' which is often + considered bad form in Python. + + Prefix-related functions: + + **'getPrefix() -> char'** -- Return the current prefix. + + **'setPrefix(char)'** -- Set a new prefix. Immediately after this + call finishes, the prefix will be changed. Changing the prefix + affects only the current interpreter; any other created + interpreters are unaffected. Setting the prefix to None or the + null string means that no further markups will be processed, + equivalent to specifying the --no-prefix command line argument. + + Diversions: + + **'stopDiverting()'** -- Any diversions that are currently taking + place are stopped; thereafter, output will go directly to the + output file as normal. It is never illegal to call this + function. + + **'createDiversion(name)'** -- Create a diversion, but do not + begin diverting to it. This is the equivalent of starting a + diversion and then immediately stopping diversion; it is used in + cases where you want to make sure that a diversion will exist + for future replaying but may be empty. + + **'startDiversion(name)'** -- Start diverting to the specified + diversion name. If such a diversion does not already exist, it + is created; if it does, then additional material will be + appended to the preexisting diversions. + + **'playDiversion(name)'** -- Recall the specified diversion and + then purge it. The provided diversion name must exist. + + **'replayDiversion(name)'** -- Recall the specified diversion + without purging it. The provided diversion name must exist. + + **'purgeDiversion(name)'** -- Purge the specified diversion + without recalling it. The provided diversion name must exist. + + **'playAllDiversions()'** -- Play (and purge) all existing + diversions in the sorted order of their names. This call does + an implicit 'empy.stopDiverting' before executing. + + **'replayAllDiversions()'** -- Replay (without purging) all + existing diversions in the sorted order of their names. This + call does an implicit 'empy.stopDiverting' before executing. + + **'purgeAllDiversions()'** -- Purge all existing diversions + without recalling them. This call does an implicit + 'empy.stopDiverting' before executing. + + **'getCurrentDiversion() -> diversion'** -- Return the name of the + current diversion. + + **'getAllDiversions() -> sequence'** -- Return a sorted list of + all existing diversions. + + Filters: + + **'getFilter() -> filter'** -- Retrieve the current filter. + 'None' indicates no filter is installed. + + **'resetFilter()'** -- Reset the filter so that no filtering is + done. + + **'nullFilter()'** -- Install a special null filter, one which + consumes all text and never sends any text to the output. + + **'setFilter(shortcut)'** -- Install a new filter. A filter is + 'None' or an empty sequence representing no filter, or '0' for a + null filter, a function for a function filter, a string for a + string filter, or an instance of 'empy.Filter' (or a workalike + object). If filter is a list of the above things, they will be + chained together manually; if it is only one, it will be + presumed to be solitary or to have already been manually chained + together. See the "Filters" section for more information. + + **'attachFilter(shortcut)'** -- Attach a single filter (sequences + are not allowed here) to the end of a currently existing filter + chain, or if there is no current chain, install it as + 'empy.setFilter' would. As with 'empy.setFilter', the shortcut + versions of filters are also allowed here. + + Hooks: + + **'areHooksEnabled()'** -- Return whether or not hooks are + presently enabled. + + **'enableHooks()'** -- Enable invocation of hooks. By default + hooks are enabled. + + **'disableHooks()'** -- Disable invocation of hooks. Hooks can + still be added, removed, and queried, but invocation of hooks + will not occur (even explicit invocation with + 'empy.invokeHook'). + + **'getHooks()'** -- Get a (copy of the) list of the hooks + currently registered. + + **'clearHooks()'** -- Clear all the hooks registered with this + interpreter. + + **'addHook(hook, prepend_opt)'** -- Add this hook to the hooks + associated with this interpreter. By default, the hook is + appended to the end of the existing hooks, if any; if the + optional insert argument is present and true, it will be + prepended to the list instead. + + **'removeHook(hook)'** -- Remove this hook from the hooks + associated with this interpreter. + + **'invokeHook(_name, ...)'** -- Manually invoke a hook method. + The remaining arguments are treated as keyword arguments and the + resulting dictionary is passed in as the second argument to the + hooks. + + Custom markup callback: + + **'getCallback() -> callback'** -- Retrieve the current callback + associated with this interpreter, or 'None' if it does not yet + have one. + + **'registerCallback(callback)'** -- Register a callback to be + called whenever a custom markup ('@<...>') is encountered. When + encountered, 'invokeCallback' is called. + + **'deregisterCallback()'** -- Clear any callback previously + registered with the interpreter for being called when a custom + markup is encountered. + + **'invokeCallback(contents)'** -- Invoke a custom callback. This + function is called whenever a custom markup ('@<...>') is + encountered. It in turn calls the registered callback, with a + single argument, 'contents', which is a string representing of + the contents of the custom markup. + + +Invocation + + Basic invocation involves running the interpreter on an EmPy file + and some optional arguments. If no file are specified, or the + file is named '-', EmPy takes its input from stdin. One can + suppress option evaluation (to, say, specify a file that begins + with a dash) by using the canonical '--' option. + + **'-h'/'--help'** -- Print usage and exit. + + **'-H'/'--extended-help'** -- Print extended usage and exit. + Extended usage includes a rundown of all the legal expansions, + escape sequences, pseudomodule contents, used hooks, and + supported environment variables. + + **'-v'/'--verbose'** -- The EmPy system will print all manner of + details about what it is doing and what it is processing to + stderr. + + **'-V'/'--version'** -- Print version and exit. + + **'-a'/'--append' (filename)** -- Open the specified file for + append instead of using stdout. + + **'-b'/'--buffered-output'** -- Fully buffer processing output, + including the file open itself. This is helpful when, should an + error occur, you wish that no output file be generated at all + (for instance, when using EmPy in conjunction with make). When + specified, either the -o or -a options must be specified, and + the -b option must precede them. This can also be specified + through the existence of the 'EMPY_BUFFERED_OUTPUT' environment + variable. + + **'-f'/'--flatten'** -- Before processing, move the contents of + the 'empy' pseudomodule into the globals, just as if + 'empy.flatten()' were executed immediately after starting the + interpreter. That is, *e.g.*, 'empy.include' can be referred to + simply as 'include' when this flag is specified on the command + line. This can also be specified through the existence of the + 'EMPY_FLATTEN' environment variable. + + **'-i'/'--interactive'** -- After the main EmPy file has been + processed, the state of the interpreter is left intact and + further processing is done from stdin. This is analogous to the + Python interpreter's -i option, which allows interactive + inspection of the state of the system after a main module is + executed. This behaves as expected when the main file is stdin + itself. This can also be specified through the existence of the + 'EMPY_INTERACTIVE' environment variable. + + **'-k'/'--suppress-errors'** -- Normally when an error is + encountered, information about its location is printed and the + EmPy interpreter exits. With this option, when an error is + encountered (except for keyboard interrupts), processing stops + and the interpreter enters interactive mode, so the state of + affairs can be assessed. This is also helpful, for instance, + when experimenting with EmPy in an interactive manner. -k + implies -i. + + **'-n'/'--no-override-stdout'** -- Do not override 'sys.stdout' + with a proxy object which the EmPy system interacts with. If + suppressed, this means that side effect printing will not be + captured and routed through the EmPy system. However, if this + option is specified, EmPy can support multithreading. + + **'-o'/'--output' (filename)** -- Open the specified file for + output instead of using stdout. If a file with that name + already exists it is overwritten. + + **'-p'/'--prefix' (prefix)** -- Change the prefix used to detect + expansions. The argument is the one-character string that will + be used as the prefix. Note that whatever it is changed to, the + way to represent the prefix literally is to double it, so if '$' + is the prefix, a literal dollar sign is represented with '$$'. + Note that if the prefix is changed to one of the secondary + characters (those that immediately follow the prefix to indicate + the type of action EmPy should take), it will not be possible to + represent literal prefix characters by doubling them (*e.g.*, if + the prefix were inadvisedly changed to '#' then '##' would + already have to represent a comment, so '##' could not represent + a literal '#'). This can also be specified through the + 'EMPY_PREFIX' environment variable. + + **'-r'/'--raw-errors'** -- Normally, EmPy catches Python + exceptions and prints them alongside an error notation + indicating the EmPy context in which it occurred. This option + causes EmPy to display the full Python traceback; this is + sometimes helpful for debugging. This can also be specified + through the existence of the 'EMPY_RAW_ERRORS' environment + variable. + + **'-u'/'--unicode'** -- Enable the Unicode subsystem. This option + only need be present if you wish to enable the Unicode subsystem + with the defaults; any other Unicode-related option (starting + with --unicode...) will also enable the Unicode subsystem. + + **'-D'/'--define' (assignment)** -- Execute a Python assignment of + the form 'variable = expression'. If only a variable name is + provided (*i.e.*, the statement does not contain an '=' sign), + then it is taken as being assigned to None. The -D option is + simply a specialized -E option that special cases the lack of an + assignment operator. Multiple -D options can be specified. + + **'-E'/'--execute' (statement)** -- Execute the Python (not EmPy) + statement before processing any files. Multiple -E options can + be specified. + + **'-F'/'--execute-file' (filename)** -- Execute the Python (not + EmPy) file before processing any files. This is equivalent to + '-E execfile("filename")' but provides a more readable context. + Multiple -F options can be specified. + + **'-I'/'--import' (module)** -- Imports the specified module name + before processing any files. Multiple modules can be specified + by separating them by commas, or by specifying multiple -I + options. + + **'-P'/'--preprocess' (filename)** -- Process the EmPy file before + processing the primary EmPy file on the command line. + + **'--binary'** -- Treat the file as a binary file, and read in + chunks rather than line by line. In this mode, the "line" + indicator represents the number of bytes read, not the number of + lines processed. + + **'--no-prefix'** -- Disable the prefixing system entirely; when + specified, EmPy will not expand any markups. This allows EmPy + to merely act as a Unicode encoding translator.. + + **'--pause-at-end'** -- If present, then 'raw_input' will be + called at the end of processing. Useful in systems where the + output window would otherwise be closed by the operating + system/window manager immediately after EmPy exited. + + **'--relative-path'** -- When present, the path the EmPy script + being invoked is contained in will be prepended to 'sys.path'. + This is analogous to Python's internal handling of 'sys.path' + and scripts. If input is from stdin ('-' for a filename or no + filename is specified), then nothing is added to the path. + + **'--no-callback-error'** -- Do not consider it an error if the + custom markup is invoked '@<...>' and there is no callback + function registered for it. + + **'--chunk-size' (chunk)** -- Use the specific binary chunk size + rather than the default; implies --binary. + + **'--unicode-encoding' (encoding)** -- Specify the Unicode + encoding to be used for both input and output. + + **'--unicode-input-encoding' (encoding)** -- Specify the Unicode + encoding to be used for input. + + **'--unicode-output-encoding' (encoding)** -- Specify the Unicode + encoding to be used for output. + + **'--unicode-input-errors (errors)** -- Specify the Unicode error + handling to be used for input. + + **'--unicode-errors (errors)** -- Specify the Unicode error + handling to be used for both input and output. + + **'--unicode-output-errors (errors)** -- Specify the Unicode error + handling to be used for output. + + +Environment variables + + EmPy also supports a few environment variables to predefine + certain behaviors. The settings chosen by environment variables + can be overridden via command line arguments. The following + environment variables have meaning to EmPy: + + **'EMPY_OPTIONS'** -- If present, the contents of this environment + variable will be treated as options, just as if they were + entered on the command line, *before* the actual command line + arguments are processed. Note that these arguments are *not* + processed by the shell, so quoting, filename globbing, and the + like, will not work. + + **'EMPY_PREFIX'** -- If present, the value of this environment + variable represents the prefix that will be used; this is + equivalent to the -p command line option. + + **'EMPY_PSEUDO'** -- If present, the value of this environment + variable represents the name of the pseudomodule that will be + incorporated into every running EmPy system; this is equivalent + to the -m command line option. + + **'EMPY_FLATTEN'** -- If defined, this is equivalent to including + -f on the command line. + + **'EMPY_RAW_ERRORS'** -- If defined, this is equivalent to + including -r on the command line. + + **'EMPY_INTERACTIVE'** -- If defined, this is equivalent to + including -i on the command line. + + **'EMPY_BUFFERED_OUTPUT'** -- If defined, this is equivalent to + including -b on the command line. + + **'EMPY_UNICODE'** -- If defined, this is equivalent to including + -u on the command line. + + **'EMPY_UNICODE_INPUT_ENCODING'** -- If present, the value of this + environment variable indicates the name of the Unicode input + encoding to be used. This is equivalent to the + --unicode-input-encoding command line option. + + **'EMPY_UNICODE_OUTPUT_ENCODING'** -- If present, the value of + this environment variable indicates the name of the Unicode + output encoding to be used. This is equivalent to the + --unicode-output-encoding command line option. + + **'EMPY_UNICODE_INPUT_ERRORS'** -- If present, the value of this + environment variable indicates the name of the error handler to + be used for input. This is equivalent to the + --unicode-input-errors command line option. + + **'EMPY_UNICODE_OUTPUT_ERRORS'** -- If present, the value of this + environment variable indicates the name of the error handler to + be used for output. This is equivalent to the + --unicode-output-errors command line option. + + +Examples and testing EmPy + + See the sample EmPy file 'sample.em' which is included with the + distribution. Run EmPy on it by typing something like:: + + ./em.py sample.em + + and compare the results and the sample source file side by side. + The sample content is intended to be self-documenting, and even an + introduction to the basic features of EmPy while simultaneously + exercising them. + + The file 'sample.bench' is the benchmark output of the sample. + Running the EmPy interpreter on the provided 'sample.em' file + should produce precisely the same results. You can run the + provided test script to see if your EmPy environment is behaving + as expected (presuming a Unix-like operating system):: + + ./test.sh + + By default this will test with the first Python interpreter + available in the path; if you want to test with another + interpreter, you can provide it as the first argument on the + command line, *e.g.*:: + + ./test.sh python2.1 + ./test.sh /usr/bin/python1.5 + ./test.sh jython + + A more comprehensive test suite and set of real-world examples is + planned for a future version. + + +Embedding EmPy + + For atomic applications, the 'expand' function is provided (the + extra keyword arguments passed in are treated as locals):: + + import em + print em.expand("@x + @y is @(x + y).", x=2, y=3) + + One can specify a globals dictionary and all the other interpreter + options (below) as well. One can specify a globals dictionary + that will be used if one wants persistence:: + + import em + g = {} + em.expand("@{x = 10}", g) + print em.expand("x is @x.", g) + + The standalone 'expand' function, however, creates and destroys an + 'Interpreter' instance each time it is called. For repeated + expansions, this can be expensive. Instead, you will probably + want to use the full-fledged features of embedding. An EmPy + interpreter can be created with as code as simple as:: + + import em + interpreter = em.Interpreter() + # The following prints the results to stdout: + interpreter.string("@{x = 123}@x\n") + # This expands to the same thing, but puts the results as a + # string in the variable result: + result = interpreter.expand("@{x = 123}@x\n") + # This just prints the value of x directly: + print interpreter.globals['x'] + # Process an actual file (and output to stdout): + interpreter.file(open('/path/to/some/file')) + interpreter.shutdown() # this is important; see below + + One can capture the output of a run in something other than stdout + by specifying the *output* parameter:: + + import em, StringIO + output = StringIO.StringIO() + interpreter = em.Interpreter(output=output) + # Do something. + interpreter.file(open('/path/to/some/file')) + interpreter.shutdown() # again, this is important; see below + print output.getvalue() # this is the result from the session + + When you are finished with your interpreter, it is important to + call its shutdown method:: + + interpreter.shutdown() + + This will ensure that the interpreter cleans up all its overhead, + entries in the 'sys.stdout' proxy, and so forth. It is usually + advisable that this be used in a try...finally clause:: + + interpreter = em.Interpreter(...) + try: + ... + finally: + interpreter.shutdown() + + The 'em.Interpreter' constructor takes the following arguments; + all are optional. Since options may be added in the future, it is + highly recommended that the constructor be invoked via keyword + arguments, rather than assuming their order. The arguments are: + + *output* -- The output file which the interpreter will be sending + all its processed data to. This need only be a file-like object; + it need not be an actual file. If omitted, 'sys.__stdout__' is + used. + + *argv* -- An argument list analogous to 'sys.argv', consisting of + the script name and zero or more arguments. These are available + to executing interpreters via 'empy.argv' and 'empy.args'. If + omitted, a non-descript script name is used with no arguments. + + *prefix* -- The prefix (a single-character string). Defaults to + '@'. It is an error for this to be anything other than one + character. + + *pseudo* -- The name (string) of the pseudmodule. Defaults to + 'empy'. + + *options* -- A dictionary of options that can override the default + behavior of the interpreter. The names of the options are + constant names ending in '_OPT' and their defaults are given in + 'Interpreter.DEFAULT_OPTIONS'. + + *globals* -- By default, interpreters begin with a pristine + dictionary of globals (except, of course, for the 'empy' + pseudomodule). Specifying this argument will allow the globals + to start with more. + + *hooks* -- A sequence of hooks (or 'None' for none) to register + with the interpreter at startup. Hooks can, of course, be added + after the fact, but this allows the hooks to intercept the + 'atStartup' event (otherwise, the startup event would already + have occurred by the time new hooks could be registered).. + + Many things can be done with EmPy interpreters; for the full + developer documentation, see the generated documentation for the + 'em' module. + + +Interpreter options + + The following options (passed in as part of the options dictionary + to the Interpreter constructor) have the following meanings. The + defaults are shown below and are also indicated in an + 'Interpreter.DEFAULT_OPTIONS' dictionary. + + **'BANGPATH_OPT'** -- Should a bangpath ('#!') as the first line + of an EmPy file be treated as if it were an EmPy comment? Note + that '#!' sequences starting lines or appearing anywhere else in + the file are untouched regardless of the value of this option. + Default: true. + + **'BUFFERED_OPT'** -- Should an 'abort' method be called upon + failure? This relates to the fully-buffered option, where all + output can be buffered including the file open; this option only + relates to the interpreter's behavior *after* that proxy file + object has been created. Default: false. + + **'RAW_OPT'** -- Should errors be displayed as raw Python errors + (that is, the exception is allowed to propagate through to the + toplevel so that the user gets a standard Python traceback)? + Default: false. + + **'EXIT_OPT'** -- Upon an error, should execution continue + (although the interpreter stacks will be purged)? Note that + even in the event this is set, the interpreter will halt upon + receiving a 'KeyboardInterrupt'. Default: true. + + **'FLATTEN_OPT'** -- Upon initial startup, should the 'empy' + pseudomodule namespace be flattened, *i.e.*, should + 'empy.flatten' be called? Note this option only has an effect + when the interpreter is first created; thereafter it is + ignored. Default: false. + + **'OVERRIDE_OPT'** -- Should the 'sys.stdout' object be overridden + with a proxy object? If not, side effect output cannot be + captured by the EmPy system, but EmPy will support + multithreading. Default: true. + + **'CALLBACK_OPT'** -- If a callback is invoked when none has yet + been registered, should an error be raised or should the + situation be ignored? Default: true. + + +Data flow + + **input -> interpreter -> diversions -> filters -> output** + + Here, in summary, is how data flows through a working EmPy system: + + 1. Input comes from a source, such an .em file on the command + line, or via an 'empy.include' statement. + + 2. The interpreter processes this material as it comes in, + expanding EmPy expansions as it goes. + + 3. After interpretation, data is then sent through the diversion + layer, which may allow it directly through (if no diversion is + in progress) or defer it temporarily. Diversions that are + recalled initiate from this point. + + 4. Any filters in place are then used to filter the data and + produce filtered data as output. + + 5. Finally, any material surviving this far is sent to the output + stream. That stream is stdout by default, but can be changed + with the -o or -a options, or may be fully buffered with the -b + option (that is, the output file would not even be opened until + the entire system is finished). + + +Author's notes + + I originally conceived EmPy as a replacement for my "Web + templating system", http://www.alcyone.com/max/info/m4.html which + uses "m4", http://www.seindal.dk/rene/gnu/ (a general + macroprocessing system for Unix). + + Most of my Web sites include a variety of m4 files, some of which + are dynamically generated from databases, which are then scanned + by a cataloging tool to organize them hierarchically (so that, + say, a particular m4 file can understand where it is in the + hierarchy, or what the titles of files related to it are without + duplicating information); the results of the catalog are then + written in database form as an m4 file (which every other m4 file + implicitly includes), and then GNU make converts each m4 to an + HTML file by processing it. + + As the Web sites got more complicated, the use of m4 (which I had + originally enjoyed for the challenge and abstractness) really + started to become an impediment to serious work; while I am very + knowledgeable about m4 -- having used it for for so many years -- + getting even simple things done with it is awkward and difficult. + Worse yet, as I started to use Python more and more over the + years, the cataloging programs which scanned the m4 and built m4 + databases were migrated to Python and made almost trivial, but + writing out huge awkward tables of m4 definitions simply to make + them accessible in other m4 scripts started to become almost + farcical -- especially when coupled with the difficulty in getting + simple things done in m4. + + It occurred to me what I really wanted was an all-Python solution. + But replacing what used to be the m4 files with standalone Python + programs would result in somewhat awkward programs normally + consisting mostly of unprocessed text punctuated by small portions + where variables and small amounts of code need to be substituted. + Thus the idea was a sort of inverse of a Python interpreter: a + program that normally would just pass text through unmolested, but + when it found a special signifier would execute Python code in a + normal environment. I looked at existing Python templating + systems, and didn't find anything that appealed to me -- I wanted + something where the desired markups were simple and unobtrusive. + After considering between choices of signifiers, I settled on '@' + and EmPy was born. + + As I developed the tool, I realized it could have general appeal, + even to those with widely varying problems to solve, provided the + core tool they needed was an interpreter that could embed Python + code inside templated text. As I continue to use the tool, I have + been adding features as unintrusively as possible as I see areas + that can be improved. + + A design goal of EmPy is that its feature set should work on + several levels; at each level, if the user does not wish or need + to use features from another level, they are under no obligation + to do so. If you have no need of diversions, for instance, you + are under no obligation to use them. If significators will not + help you organize a set of EmPy scripts globally, then you need + not use them. New features that are being added are whenever + possible transparently backward compatible; if you do not need + them, their introduction should not affect you in any way. The + use of unknown prefix sequences results in errors, guaranteeing + that they are reserved for future use. + + +Glossary + + **control** -- A control markup, used to direct high-level control + flow within an EmPy session. Control markups are expressed with + the '@[...]' notation. + + **diversion** -- A process by which output is deferred, and can be + recalled later on demand, multiple times if necessary. + + **document** -- The abstraction of an EmPy document as used by a + processor. + + **escape** -- A markup designed to expand to a single (usually + non-printable) character, similar to escape sequences in C or + other languages. + + **expansion** -- The process of processing EmPy markups and + producing output. + + **expression** -- An expression markup represents a Python + expression to be evaluated, and replaced with the 'str' of its + value. Expression markups are expressed with the '@(...)' + notation. + + **filter** -- A file-like object which can be chained to other + objects (primarily the final stream) and can buffer, alter, or + manipulate in any way the data sent. Filters can also be + chained together in arbitrary order. + + **globals** -- The dictionary (or dictionary-like object) which + resides inside the interpreter and holds the currently-defined + variables. + + **hook** -- A callable object that can be registered in a + dictionary, and which will be invoked before, during, or after + certain internal operations, identified by name with a string. + + **interpreter** -- The application (or class instance) which + processes EmPy markup. + + **markup** -- EmPy substitutions set off with a prefix and + appropriate delimeters. + + **output** -- The final destination of the result of processing an + EmPy file. + + **prefix** -- The ASCII character used to set off an expansions. + By default, '@'. + + **processor** -- An extensible system which processes a group of + EmPy files, usually arranged in a filesystem, and scans them for + significators. + + **pseudomodule** -- The module-like object named 'empy' which is + exposed internally inside every EmPy system. + + **shortcut** -- A special object which takes the place of an + instance of the 'Filter' class, to represent a special form of + filter. These include 0 for a null filter, a callable (function + or lambda) to represent a callable filter, or a 256-character + string which represents a translation filter. + + **significator** -- A special form of an assignment markup in EmPy + which can be easily parsed externally, primarily designed for + representing uniform assignment across a collection of files. + Significators are indicated with the '@%' markup. + + **statement** -- A line of code that needs to be executed; + statements do not have return values. In EmPy, statements are + set off with '@{...}'. + + +Acknowledgements + + Questions, suggestions, bug reports, evangelism, and even + complaints from many people have helped make EmPy what it is + today. Some, but by no means all, of these people are (in + alphabetical order by surname): + + - Biswapesh Chattopadhyay + + - Beni Cherniavsky + + - Dr. S. Candelaria de Ram + + - Eric Eide + + - Dinu Gherman + + - Grzegorz Adam Hankiewicz + + - Bohdan Kushnir + + - Robert Kroeger + + - Kouichi Takahashi + + - Ville Vainio + + +Known issues and caveats + + - EmPy was primarily intended for static processing of documents, + rather than dynamic use, and hence speed of processing was not + the primary consideration in its design. + + - EmPy is not threadsafe by default. This is because of the need + for EmPy to override the 'sys.stdout' file with a proxy object + which can capture effects of 'print' and other spooling to + stdout. This proxy can be suppressed with the -n option, which + will result in EmPy being unable to do anything meaningful with + this output, but will allow EmPy to be threadsafe. + + - To function properly, EmPy must override 'sys.stdout' with a + proxy file object, so that it can capture output of side effects + and support diversions for each interpreter instance. It is + important that code executed in an environment *not* rebind + 'sys.stdout', although it is perfectly legal to invoke it + explicitly (*e.g.*, '@sys.stdout.write("Hello world\n")'). If + one really needs to access the "true" stdout, then use + 'sys.__stdout__' instead (which should also not be rebound). + EmPy uses the standard Python error handlers when exceptions are + raised in EmPy code, which print to 'sys.stderr'. + + - Due to Python's curious handling of the 'print' statement -- + particularly the form with a trailing comma to suppress the + final newline -- mixing statement expansions using prints inline + with unexpanded text will often result in surprising behavior, + such as extraneous (sometimes even deferred!) spaces. This is a + Python "feature," and occurs in non-EmPy applications as well; + for finer control over output formatting, use 'sys.stdout.write' + or 'empy.interpreter.write' directly. + + - The 'empy' "module" exposed through the EmPy interface (*e.g.*, + '@empy') is an artificial module. It cannot be imported with + the 'import' statement (and shouldn't -- it is an artifact of + the EmPy processing system and does not correspond to any + accessible .py file). + + - For an EmPy statement expansion all alone on a line, *e.g.*, + '@{a = 1}', note that this will expand to a blank line due to + the newline following the closing curly brace. To suppress this + blank line, use the symmetric convention '@{a = 1}@'. + + - When using EmPy with make, note that partial output may be + created before an error occurs; this is a standard caveat when + using make. To avoid this, write to a temporary file and move + when complete, delete the file in case of an error, use the -b + option to fully buffer output (including the open), or (with GNU + make) define a '.DELETE_ON_ERROR' target. + + - 'empy.identify' tracks the context of executed *EmPy* code, not + Python code. This means that blocks of code delimited with '@{' + and '}' will identify themselves as appearing on the line at + which the '}' appears, and that pure Python code executed via + the -D, -E and -F command line arguments will show up as all taking + place on line 1. If you're tracking errors and want more + information about the location of the errors from the Python + code, use the -r command line option, which will provide you + with the full Python traceback. + + - The conditional form of expression expansion '@(...?...!...)' + allows the use of a colon instead of an exclamation point, + *e.g.*, '@(...?...:...)'. This behavior is supported for + backward compatibility, but is deprecated. Due to an oversight, + the colon was a poor choice since colons can appear legally in + expressions (*e.g.*, dictionary literals or lambda expressions). + + - The '@[try]' construct only works with Python exceptions derived + from 'Exception'. It is not able to catch string exceptions. + + - The '@[for]' variable specification supports tuples for tuple + unpacking, even recursive tuples. However, it is limited in + that the names included may only be valid Python identifiers, + not arbitrary Python lvalues. Since the internal Python + mechanism is very rarely used for this purpose (*e.g.*, 'for (x, + l[0], q.a) in sequence'), it is not thought to be a significant + limitation. + + +Wish list + + Here are some random ideas for future revisions of EmPy. If any + of these are of particular interest to you, your input would be + appreciated. + + - Some real-world examples should really be included for + demonstrating the power and expressiveness of EmPy first-hand. + + - More extensive help (rather than a ridiculously long README), + probably inherently using the EmPy system itself for building to + HTML and other formats, thereby acting as a help facility and a + demonstration of the working system. + + - A "trivial" mode, where all the EmPy system does is scan for + simple symbols to replace them with evaluations/executions, + rather than having to do the contextual scanning it does now. + This has the down side of being much less configurable and + powerful but the upside of being extremely efficient. + + - A "debug" mode, where EmPy prints the contents of everything + it's about to evaluate (probably to stderr) before it does? + + - The ability to funnel all code through a configurable 'RExec' + for user-controlled security control. This would probably + involve abstracting the execution functionality outside of the + interpreter. [This suggestion is on hold until the + rexec/Bastion exploits are worked out.] + + - Optimized handling of processing would be nice for the + possibility of an Apache module devoted to EmPy processing. + + - An EmPy emacs mode. + + - An optimization of offloading diversions to files when they + become truly huge. (This is made possible by the abstraction of + the 'Diversion' class.) + + - Support for mapping filters (specified by dictionaries). + + - Support for some sort of batch processing, where several EmPy + files can be listed at once and all of them evaluated with the + same initial (presumably expensive) environment. + 'empy.saveGlobals' and 'empy.restoreGlobals' have been + introduced as a partial solution, but they need to be made more + robust. + + - A more elaborate interactive mode, perhaps with a prompt and + readline support. + + - A StructuredText and/or reStructuredText filter would be quite + useful, as would SGML/HTML/XML/XHTML, s-expression, Python, + etc. auto-indenter filters. + + - An indexing filter, which can process text and pick out + predefined keywords and thereby setup links to them. + + - The ability to rerun diverted material back through the + interpreter. (This can be done, awkwardly, by manually creating + a filter which itself contains an interpreter, but it might be + helpful if this was an all-in-one operation.) + + - A caching system that stores off the compilations of repeated + evaluations and executions so that in a persistent environment + the same code does not have to be repeatedly evaluated/executed. + This would probably be a necessity in an Apache module-based + solution. Perhaps caching even to the point of generating pure + PyWM bytecode? + + - An option to change the format of the standard EmPy errors in a + traceback. + + - Support for some manner of implicitly processed /etc/empyrc + and/or ~/.empyrc file, and of course an option to inhibit its + processing. This can already be accomplished (and with greater + control) via use of EMPY_OPTIONS, though. + + - More uniform handling of the preprocessing directives (-I, -D, + -E, -F, and -P), probably mapping directly to methods in the + 'Interpreter' class. + + - Support for integration with mod_python. + + - In simple expressions, a '{...}' suffix has no meaning in Python + (*e.g.*, in Python, '@x(...)' is a call, '@x[...]' is + subscription, but '@x{...}' is illegal). This could be + exploited by having a '{...}' suffix in a simple expression + representing an encapsulation of an expanded string; *e.g.*, + '@bullet{There are @count people here}' would be equivalent to + '@bullet(empy.expand("There are @count people here", + locals()))}'. + + - A tool to collect significator information from a hierarchy of + .em files and put them in a database form available for + individual scripts would be extremely useful -- this tool should + be extensible so that users can use it to, say, build ordered + hierarchies of their EmPy files by detecting contextual + information like application-specific links to other EmPy + documents. + + - Extensions of the basic EmPy concepts to projects for other + interpreted languages, such as Java, Lua, Ruby, and/or Perl. + + - Ignore 'SystemExit' when doing error handling, letting the + exception progagate up? So far no one seems to worry about + this; deliberately exiting early in a template seems to be an + unlikely occurrence. (Furthermore, there are the 'os.abort' and + 'os._exit' facilities for terminating without exception + propagation.) + + - A new markup which is the equivalent of '$...:...$' in source + control systems, where the left-hand portion represents a + keyword and the right-hand portion represents its value which is + substituted in by the EmPy system. + + - The ability to obtain the filename (if relevant) and mode of the + primary output file. + + - The ability to redirect multiple streams of output; not + diversions, but rather the ability to write to one file and then + another. Since output would be under the EmPy script's control, + this would imply a useful --no-output option, where by default + no output is written. This would also suggest the usefulness of + all the output file delegates (diversions, filters, abstract + files, etc.) passing unrecognized method calls all the way down + to underlying file object. + + - In addition to the em.py script, an additional support library + (non-executable) should be included which includes ancillary + functionality for more advanced features, but which is not + necessary to use EmPy in its basic form as a standalone + executable. Such features would include things like + significator processing, metadata scanning, and advanced + prompting systems. + + +Release history + + - 3.3.2; 2014 Jan 24. Additional fix for source compatibility + between 2.x and 3.0. + + - 3.3.1; 2014 Jan 22. Source compatibility for 2.x and 3.0; + 1.x and Jython compatibility dropped. + + - 3.3; 2003 Oct 27. Custom markup '@<...>'; remove separate + pseudomodule instance for greater transparency; deprecate + 'interpreter' attribute of pseudomodule; deprecate auxiliary + class name attributes associated with pseudomodule in + preparation for separate support library in 4.0; add + --no-callback-error and --no-bangpath-processing command line + options; add 'atToken' hook. + + - 3.2; 2003 Oct 7. Reengineer hooks support to use hook + instances; add -v option; add --relative-path option; reversed + PEP 317 style; modify Unicode support to give less confusing + errors in the case of unknown encodings and error handlers; + relicensed under LGPL. + + - 3.1.1; 2003 Sep 20. Add literal '@"..."' markup; add + --pause-at-end command line option; fix improper globals + collision error via the 'sys.stdout' proxy. + + - 3.1; 2003 Aug 8. Unicode support (Python 2.0 and above); add + Document and Processor helper classes for processing + significators; add --no-prefix option for suppressing all + markups. + + - 3.0.4; 2003 Aug 7. Implement somewhat more robust lvalue + parsing for '@[for]' construct (thanks to Beni Cherniavsky for + inspiration). + + - 3.0.3; 2003 Jul 9. Fix bug regarding recursive tuple unpacking + using '@[for]'; add 'empy.saveGlobals', 'empy.restoreGlobals', + and 'empy.defined' functions. + + - 3.0.2; 2003 Jun 19. '@?' and '@!' markups for changing the + current context name and line, respectively; add 'update' method + to interpreter; new and renamed context operations, + 'empy.setContextName', 'empy.setContextLine', + 'empy.pushContext', 'empy.popContext'. + + - 3.0.1; 2003 Jun 9. Fix simple bug preventing command line + preprocessing directives (-I, -D, -E, -F, -P) from executing + properly; defensive PEP 317 compliance [defunct]. + + - 3.0; 2003 Jun 1. Control markups with '@[...]'; remove + substitutions (use control markups instead); support + '@(...?...!...)' for conditional expressions in addition to the + now-deprecated '@(...?...:...)' variety; add acknowledgements + and glossary sections to documentation; rename buffering option + back to -b; add -m option and 'EMPY_PSEUDO' environment variable + for changing the pseudomodule name; add -n option and + 'EMPY_NO_OVERRIDE' environment variable for suppressing + 'sys.stdout' proxy; rename main error class to 'Error'; add + standalone 'expand' function; add --binary and --chunk-size + options; reengineer parsing system to use Tokens for easy + extensibility; safeguard curly braces in simple expressions + (meaningless in Python and thus likely a typographical error) by + making them a parse error; fix bug involving custom Interpreter + instances ignoring globals argument; distutils support. + + - 2.3; 2003 Feb 20. Proper and full support for concurrent and + recursive interpreters; protection from closing the true stdout + file object; detect edge cases of interpreter globals or + 'sys.stdout' proxy collisions; add globals manipulation + functions 'empy.getGlobals', 'empy.setGlobals', and + 'empy.updateGlobals' which properly preserve the 'empy' + pseudomodule; separate usage info out into easily accessible + lists for easier presentation; have -h option show simple usage + and -H show extened usage; add 'NullFile' utility class. + + - 2.2.6; 2003 Jan 30. Fix a bug in the 'Filter.detach' method + (which would not normally be called anyway). + + - 2.2.5; 2003 Jan 9. Strip carriage returns out of executed code + blocks for DOS/Windows compatibility. + + - 2.2.4; 2002 Dec 23. Abstract Filter interface to use methods + only; add '@[noop: ...]' substitution for completeness and block + commenting [defunct]. + + - 2.2.3; 2002 Dec 16. Support compatibility with Jython by + working around a minor difference between CPython and Jython in + string splitting. + + - 2.2.2; 2002 Dec 14. Include better docstrings for pseudomodule + functions; segue to a dictionary-based options system for + interpreters; add 'empy.clearAllHooks' and 'empy.clearGlobals'; + include a short documentation section on embedding interpreters; + fix a bug in significator regular expression. + + - 2.2.1; 2002 Nov 30. Tweak test script to avoid writing + unnecessary temporary file; add 'Interpreter.single' method; + expose 'evaluate', 'execute', 'substitute' [defunct], and + 'single' methods to the pseudomodule; add (rather obvious) + 'EMPY_OPTIONS' environment variable support; add + 'empy.enableHooks' and 'empy.disableHooks'; include optimization + to transparently disable hooks until they are actually used. + + - 2.2; 2002 Nov 21. Switched to -V option for version + information; 'empy.createDiversion' for creating initially empty + diversion; direct access to diversion objects with + 'empy.retrieveDiversion'; environment variable support; removed + --raw long argument (use --raw-errors instead); added quaternary + escape code (well, why not). + + - 2.1; 2002 Oct 18. 'empy.atExit' registry separate from hooks to + allow for normal interpreter support; include a benchmark sample + and test.sh verification script; expose 'empy.string' directly; + -D option for explicit defines on command line; remove + ill-conceived support for '@else:' separator in '@[if ...]' + substitution [defunct] ; handle nested substitutions properly + [defunct] ; '@[macro ...]' substitution for creating recallable + expansions [defunct]. + + - 2.0.1; 2002 Oct 8. Fix missing usage information; fix + after_evaluate hook not getting called; add 'empy.atExit' call + to register values. + + - 2.0; 2002 Sep 30. Parsing system completely revamped and + simplified, eliminating a whole class of context-related bugs; + builtin support for buffered filters; support for registering + hooks; support for command line arguments; interactive mode with + -i; significator value extended to be any valid Python + expression. + + - 1.5.1; 2002 Sep 24. Allow '@]' to represent unbalanced close + brackets in '@[...]' markups [defunct]. + + - 1.5; 2002 Sep 18. Escape codes ('@\...'); conditional and + repeated expansion substitutions [defunct] ; replaced with control + markups]; fix a few bugs involving files which do not end in + newlines. + + - 1.4; 2002 Sep 7. Fix bug with triple quotes; collapse + conditional and protected expression syntaxes into the single + generalized '@(...)' notation; 'empy.setName' and 'empy.setLine' + functions [deprecated] ; true support for multiple concurrent + interpreters with improved sys.stdout proxy; proper support for + 'empy.expand' to return a string evaluated in a subinterpreter + as intended; merged Context and Parser classes together, and + separated out Scanner functionality. + + - 1.3; 2002 Aug 24. Pseudomodule as true instance; move toward + more verbose (and clear) pseudomodule functions; fleshed out + diversion model; filters; conditional expressions; protected + expressions; preprocessing with -P (in preparation for + possible support for command line arguments). + + - 1.2; 2002 Aug 16. Treat bangpaths as comments; 'empy.quote' for + the opposite process of 'empy.expand'; significators ('@%...' + sequences); -I option; -f option; much improved documentation. + + - 1.1.5; 2002 Aug 15. Add a separate 'invoke' function that can be + called multiple times with arguments to simulate multiple runs. + + - 1.1.4; 2002 Aug 12. Handle strings thrown as exceptions + properly; use getopt to process command line arguments; cleanup + file buffering with AbstractFile; very slight documentation and + code cleanup. + + - 1.1.3; 2002 Aug 9. Support for changing the prefix from within + the 'empy' pseudomodule. + + - 1.1.2; 2002 Aug 5. Renamed buffering option [defunct], added -F + option for interpreting Python files from the command line, + fixed improper handling of exceptions from command line options + (-E, -F). + + - 1.1.1; 2002 Aug 4. Typo bugfixes; documentation clarification. + + - 1.1; 2002 Aug 4. Added option for fully buffering output + (including file opens), executing commands through the command + line; some documentation errors fixed. + + - 1.0; 2002 Jul 23. Renamed project to EmPy. Documentation and + sample tweaks; added 'empy.flatten'. Added -a option. + + - 0.3; 2002 Apr 14. Extended "simple expression" syntax, + interpreter abstraction, proper context handling, better error + handling, explicit file inclusion, extended samples. + + - 0.2; 2002 Apr 13. Bugfixes, support non-expansion of Nones, + allow choice of alternate prefix. + + - 0.1.1; 2002 Apr 12. Bugfixes, support for Python 1.5.x, add -r + option. + + - 0.1; 2002 Apr 12. Initial early access release. + + +Author + + This module was written by "Erik Max Francis", + http://www.alcyone.com/max/. If you use this software, have + suggestions for future releases, or bug reports, "I'd love to hear + about it", mailto:software@alcyone.com. + + Even if you try out EmPy for a project and find it unsuitable, I'd + like to know what stumbling blocks you ran into so they can + potentially be addressed in a future version. + + +Version + + Version 3.3.2 $Date: 2014-01-24 13:39:38 -0800 (Fri, 24 Jan 2014) $ $Author: max $ diff --git a/doc/home/max/projects/empy/doc/em.html b/doc/home/max/projects/empy/doc/em.html new file mode 100644 index 0000000..fd79b90 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em.html @@ -0,0 +1,506 @@ + + + + + + Module: em + + + + +

Table of Contents

+ + + + + + + + + +
Module: em + em.py +
+ +

A system for processing Python as markup embedded in text.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Imported modules  + + +   +
+ +import copy
+import getopt
+import os
+import re
+import string
+import sys
+import types
+ +
+ + Functions  + + +   +
+ +environment
+expand
+info
+invoke
+main
+usage
+ + +
+ +   + + + environment  +
+
+environment ( name,  default=None )
+
+
+ +

Get data from the current environment. If the default is True + or False, then presume that we're only interested in the existence + or non-existence of the environment variable.

+ +
+ +   + + + expand  +
+
+expand (
+        _data,
+        _globals=None,
+        _argv=None,
+        _prefix=DEFAULT_PREFIX,
+        _pseudo=None,
+        _options=None,
+        **_locals,
+        )
+
+
+ +

Do an atomic expansion of the given source data, creating and + shutting down an interpreter dedicated to the task. The sys.stdout + object is saved off and then replaced before this function + returns.

+ +
+ +   + + + info  +
+
+info ( table )
+
+
+ + +
+ +   + + + invoke  +
+
+invoke ( args )
+
+
+ +

Run a standalone instance of an EmPy interpeter.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +Error, "prefix must be single-character string"
+ValueError, "-b only makes sense with -o or -a arguments"
+ +
+ +
+ +   + + + main  +
+
+main ()
+
+
+ + +
+ +   + + + usage  +
+
+usage ( verbose=True )
+
+
+ +

Print usage information.

+
+ + Classes  + + +   +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

AbstractFile

+

An abstracted file that, when buffered, will totally buffer the

+

BreakFlow

+

A break control flow.

+

BufferedFilter

+

A buffered filter is one that doesn't modify the source data

+

CommentToken

+

A comment markup.

+

Context

+

An interpreter context, which encapsulates a name, an input

+

ContextLineToken

+

A context line change markup.

+

ContextNameToken

+

A context name change markup.

+

ContinueFlow

+

A continue control flow.

+

ControlToken

+

A control token.

+

CustomToken

+

A custom markup.

+

Diversion

+

The representation of an active diversion. Diversions act as

+

DiversionError

+

An error related to diversions.

+

Document

+

A representation of an individual EmPy document, as used by a

+

Error

+

The base class for all EmPy errors.

+

EscapeToken

+

An escape markup.

+

ExpansionToken

+

A token that involves an expansion.

+

ExpressionToken

+

An expression markup.

+

Filter

+

An abstract filter.

+

FilterError

+

An error related to filters.

+

FlowError

+

An exception related to control flow.

+

FunctionFilter

+

A filter that works simply by pumping its input through a

+

Hook

+

The base class for implementing hooks.

+

InPlaceToken

+

An in-place markup.

+

Interpreter

+

An interpreter can process chunks of EmPy code.

+

LineBufferedFilter

+

A line-buffered filter only lets data through when it sees

+

LiteralToken

+

A literal markup.

+

MaximallyBufferedFilter

+

A maximally-buffered filter only lets its data through on the final

+

MetaError

+

A wrapper around a real Python exception for including a copy of

+

NullFile

+

A simple class that supports all the file-like object methods

+

NullFilter

+

A filter that never sends any output to its sink.

+

NullToken

+

A chunk of data not containing markups.

+

ParseError

+

A parse error occurred.

+

PrefixToken

+

A prefix markup.

+

Processor

+

An entity which is capable of processing a hierarchy of EmPy

+

ProxyFile

+

The proxy file object that is intended to take the place of

+

ReprToken

+

A repr markup.

+

Scanner

+

A scanner holds a buffer for lookahead parsing and has the

+

SignificatorToken

+

A significator markup.

+

SimpleExpressionToken

+

A simple expression markup.

+

SizeBufferedFilter

+

A size-buffered filter only in fixed size chunks (excepting the

+

Stack

+

A simple stack that behaves as a sequence (with 0 being the top

+

StackUnderflowError

+

A stack underflow.

+

StatementToken

+

A statement markup.

+

Stream

+

A wrapper around an (output) file object which supports

+

StringFilter

+

A filter that takes a translation string (256 characters) and

+

StringLiteralToken

+

A string token markup.

+

Subsystem

+

The subsystem class defers file creation so that it can create

+

SubsystemError

+

An error associated with the Unicode subsystem.

+

Token

+

An element of expansion.

+

TransientParseError

+

A parse error occurred which may be resolved by feeding more data.

+

UncloseableFile

+

A simple class which wraps around a delegate file-like object

+

VerboseHook

+

A verbose hook that reports all information received by the

+

WhitespaceToken

+

A whitespace markup.

+
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/AbstractFile.html b/doc/home/max/projects/empy/doc/em/AbstractFile.html new file mode 100644 index 0000000..b5185bc --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/AbstractFile.html @@ -0,0 +1,305 @@ + + + + + + Class: AbstractFile + + + + +

Table of Contents

+ + + + + + + + + +
Class: AbstractFile + em.py +
+ +

An abstracted file that, when buffered, will totally buffer the + file, including even the file open.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__del__
+__init__
+abort
+close
+commit
+flush
+write
+writelines
+ + +
+ +   + + + __del__  +
+
+__del__ ( self )
+
+
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        filename,
+        mode='w',
+        buffered=False,
+        )
+
+
+ + +
+ +   + + + abort  +
+
+abort ( self )
+
+
+ + +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + commit  +
+
+commit ( self )
+
+
+ + +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/BreakFlow.html b/doc/home/max/projects/empy/doc/em/BreakFlow.html new file mode 100644 index 0000000..dd4b986 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/BreakFlow.html @@ -0,0 +1,88 @@ + + + + + + Class: BreakFlow + + + + +

Table of Contents

+ + + + + + + + + +
Class: BreakFlow + em.py +
+ +

A break control flow.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +FlowError
+
    + +Error
    +
      + +Exception
      + +
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/BufferedFilter.html b/doc/home/max/projects/empy/doc/em/BufferedFilter.html new file mode 100644 index 0000000..0e11d99 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/BufferedFilter.html @@ -0,0 +1,190 @@ + + + + + + Class: BufferedFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: BufferedFilter + em.py +
+ +

A buffered filter is one that doesn't modify the source data + sent to the sink, but instead holds it for a time. The standard + variety only sends the data along when it receives a flush + command.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Filter
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+flush
+write
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/CommentToken.html b/doc/home/max/projects/empy/doc/em/CommentToken.html new file mode 100644 index 0000000..b153bd6 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/CommentToken.html @@ -0,0 +1,192 @@ + + + + + + Class: CommentToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: CommentToken + em.py +
+ +

A comment markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +scan
+string
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "comment expects newline"
+ +
+ +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Context.html b/doc/home/max/projects/empy/doc/em/Context.html new file mode 100644 index 0000000..fcfd1ee --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Context.html @@ -0,0 +1,193 @@ + + + + + + Class: Context + + + + +

Table of Contents

+ + + + + + + + + +
Class: Context + em.py +
+ +

An interpreter context, which encapsulates a name, an input + file object, and a parser object.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+__str__
+bump
+identify
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        name,
+        line=0,
+        units=DEFAULT_UNIT,
+        )
+
+
+ + +
+ +   + + + __str__  +
+
+__str__ ( self )
+
+
+ + +
+ +   + + + bump  +
+
+bump ( self,  quantity=1 )
+
+
+ + +
+ +   + + + identify  +
+
+identify ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ContextLineToken.html b/doc/home/max/projects/empy/doc/em/ContextLineToken.html new file mode 100644 index 0000000..b2f17cd --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ContextLineToken.html @@ -0,0 +1,197 @@ + + + + + + Class: ContextLineToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ContextLineToken + em.py +
+ +

A context line change markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "context line requires integer"
+TransientParseError, "context line expects newline"
+ +
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ContextNameToken.html b/doc/home/max/projects/empy/doc/em/ContextNameToken.html new file mode 100644 index 0000000..f755525 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ContextNameToken.html @@ -0,0 +1,196 @@ + + + + + + Class: ContextNameToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ContextNameToken + em.py +
+ +

A context name change markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "context name expects newline"
+ +
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ContinueFlow.html b/doc/home/max/projects/empy/doc/em/ContinueFlow.html new file mode 100644 index 0000000..aee37e2 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ContinueFlow.html @@ -0,0 +1,88 @@ + + + + + + Class: ContinueFlow + + + + +

Table of Contents

+ + + + + + + + + +
Class: ContinueFlow + em.py +
+ +

A continue control flow.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +FlowError
+
    + +Error
    +
      + +Exception
      + +
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ControlToken.html b/doc/home/max/projects/empy/doc/em/ControlToken.html new file mode 100644 index 0000000..ff760b7 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ControlToken.html @@ -0,0 +1,457 @@ + + + + + + Class: ControlToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ControlToken + em.py +
+ +

A control token.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +build
+run
+scan
+string
+subrun
+subscan
+substring
+ + +
+ +   + + + build  +
+
+build ( self,  allowed=None )
+
+
+ +

Process the list of subtokens and divide it into a list of + 2-tuples, consisting of the dividing tokens and the list of + subtokens that follow them. If allowed is specified, it will + represent the list of the only secondary markup types which + are allowed.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "control unexpected secondary: '%s'" % subtoken.type
+ +
+ +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ + + + + + + +
+BreakFlow, "control 'break' without 'for', 'while'"
+ContinueFlow, "control 'continue' without 'for', 'while'"
+ParseError, "control '%s' cannot be at this level" % self.type
+ParseError, "control 'end' requires primary markup"
+ParseError, "control 'for' expects at most one 'else'"
+ParseError, "control 'if' unexpected secondary: '%s'" % secondary.type
+
+ParseError, "control 'try' can only have one 'finally'"
+ParseError, "control 'try' cannot have 'except' and 'finally'"
+ParseError, "control 'try' needs 'except' or 'finally'"
+ParseError, "control 'while' expects at most one 'else'"
+ParseError, "control expected 'for x in seq'"
+
+ +
+ +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "control '%s' needs arguments" % self.type
+ParseError, "unknown control markup: '%s'" % self.type
+ +
+ +
+ +   + + + string  +
+
+string ( self )
+
+
+ + +
+ +   + + + subrun  +
+
+subrun (
+        self,
+        tokens,
+        interpreter,
+        locals,
+        )
+
+
+ +

Execute a sequence of tokens.

+ +
+ +   + + + subscan  +
+
+subscan (
+        self,
+        scanner,
+        primary,
+        )
+
+
+ +

Do a subscan for contained tokens.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "control must end with 'end %s'" % primary
+TransientParseError, "control '%s' needs more tokens" % primary
+ +
+ +
+ +   + + + substring  +
+
+substring ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/CustomToken.html b/doc/home/max/projects/empy/doc/em/CustomToken.html new file mode 100644 index 0000000..c4e1ce7 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/CustomToken.html @@ -0,0 +1,196 @@ + + + + + + Class: CustomToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: CustomToken + em.py +
+ +

A custom markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Diversion.html b/doc/home/max/projects/empy/doc/em/Diversion.html new file mode 100644 index 0000000..6e648e4 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Diversion.html @@ -0,0 +1,275 @@ + + + + + + Class: Diversion + + + + +

Table of Contents

+ + + + + + + + + +
Class: Diversion + em.py +
+ +

The representation of an active diversion. Diversions act as + (writable) file objects, and then can be recalled either as pure + strings or (readable) file objects.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+asFile
+asString
+close
+flush
+write
+writelines
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + asFile  +
+
+asFile ( self )
+
+
+ +

Return the diversion as a file.

+ +
+ +   + + + asString  +
+
+asString ( self )
+
+
+ +

Return the diversion as a string.

+ +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/DiversionError.html b/doc/home/max/projects/empy/doc/em/DiversionError.html new file mode 100644 index 0000000..a3872f3 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/DiversionError.html @@ -0,0 +1,83 @@ + + + + + + Class: DiversionError + + + + +

Table of Contents

+ + + + + + + + + +
Class: DiversionError + em.py +
+ +

An error related to diversions.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Document.html b/doc/home/max/projects/empy/doc/em/Document.html new file mode 100644 index 0000000..68ce2bb --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Document.html @@ -0,0 +1,108 @@ + + + + + + Class: Document + + + + +

Table of Contents

+ + + + + + + + + +
Class: Document + em.py +
+ +

A representation of an individual EmPy document, as used by a + processor.

+ + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        ID,
+        filename,
+        )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Error.html b/doc/home/max/projects/empy/doc/em/Error.html new file mode 100644 index 0000000..b548dc5 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Error.html @@ -0,0 +1,78 @@ + + + + + + Class: Error + + + + +

Table of Contents

+ + + + + + + + + +
Class: Error + em.py +
+ +

The base class for all EmPy errors.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Exception
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/EscapeToken.html b/doc/home/max/projects/empy/doc/em/EscapeToken.html new file mode 100644 index 0000000..2c8b1d2 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/EscapeToken.html @@ -0,0 +1,228 @@ + + + + + + Class: EscapeToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: EscapeToken + em.py +
+ +

An escape markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "invalid escape control code"
+ParseError, "invalid numeric escape code"
+ParseError, "unrecognized escape code"
+ParseError, r"Unicode name escape should be \N{...}"
+SubsystemError, "unknown Unicode character name: %s" % name
+ +
+ +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ExpansionToken.html b/doc/home/max/projects/empy/doc/em/ExpansionToken.html new file mode 100644 index 0000000..1bb933e --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ExpansionToken.html @@ -0,0 +1,195 @@ + + + + + + Class: ExpansionToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ExpansionToken + em.py +
+ +

A token that involves an expansion.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Token
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+run
+scan
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        prefix,
+        first,
+        )
+
+
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ExpressionToken.html b/doc/home/max/projects/empy/doc/em/ExpressionToken.html new file mode 100644 index 0000000..8f2e19a --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ExpressionToken.html @@ -0,0 +1,196 @@ + + + + + + Class: ExpressionToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ExpressionToken + em.py +
+ +

An expression markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Filter.html b/doc/home/max/projects/empy/doc/em/Filter.html new file mode 100644 index 0000000..a372157 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Filter.html @@ -0,0 +1,431 @@ + + + + + + Class: Filter + + + + +

Table of Contents

+ + + + + + + + + +
Class: Filter + em.py +
+ +

An abstract filter.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + +
+__init__
+_flush
+attach
+close
+detach
+
+flush
+last
+next
+write
+writelines
+
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +NotImplementedError
+ +
+ +
+ +   + + + _flush  +
+
+_flush ( self )
+
+
+ +

The _flush method should always flush the sink and should not + be overridden.

+ +
+ +   + + + attach  +
+
+attach ( self,  filter )
+
+
+ +

Attach a filter to this one.

+ +
+ +   + + + close  +
+
+close ( self )
+
+
+ +

Close the filter. Do an explicit flush first, then close the + sink.

+ +
+ +   + + + detach  +
+
+detach ( self )
+
+
+ +

Detach a filter from its sink.

+ +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ +

The flush method can be overridden.

+ +
+ +   + + + last  +
+
+last ( self )
+
+
+ +

Find the last filter in this chain.

+ +
+ +   + + + next  +
+
+next ( self )
+
+
+ +

Return the next filter/file-like object in the sequence, or None.

+ +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +

The standard write method; this must be overridden in subclasses.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +NotImplementedError
+ +
+ +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +

Standard writelines wrapper.

+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/FilterError.html b/doc/home/max/projects/empy/doc/em/FilterError.html new file mode 100644 index 0000000..017eeeb --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/FilterError.html @@ -0,0 +1,83 @@ + + + + + + Class: FilterError + + + + +

Table of Contents

+ + + + + + + + + +
Class: FilterError + em.py +
+ +

An error related to filters.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/FlowError.html b/doc/home/max/projects/empy/doc/em/FlowError.html new file mode 100644 index 0000000..d990200 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/FlowError.html @@ -0,0 +1,83 @@ + + + + + + Class: FlowError + + + + +

Table of Contents

+ + + + + + + + + +
Class: FlowError + em.py +
+ +

An exception related to control flow.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/FunctionFilter.html b/doc/home/max/projects/empy/doc/em/FunctionFilter.html new file mode 100644 index 0000000..cad597a --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/FunctionFilter.html @@ -0,0 +1,160 @@ + + + + + + Class: FunctionFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: FunctionFilter + em.py +
+ +

A filter that works simply by pumping its input through a + function which maps strings into strings.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Filter
+ +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+write
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  function )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Hook.html b/doc/home/max/projects/empy/doc/em/Hook.html new file mode 100644 index 0000000..b60ebe6 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Hook.html @@ -0,0 +1,1655 @@ + + + + + + Class: Hook + + + + +

Table of Contents

+ + + + + + + + + +
Class: Hook + em.py +
+ +

The base class for implementing hooks.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + + +
+__init__
+afterAtomic
+afterBinary
+afterClause
+afterControl
+afterDefined
+afterEscape
+afterEvaluate
+afterExecute
+afterExpand
+afterFile
+afterImport
+afterInclude
+afterLiteral
+afterMulti
+afterQuote
+afterSerialize
+afterSignificate
+
+afterSingle
+afterString
+atFinalize
+atHandle
+atInteract
+atParse
+atReady
+atShutdown
+atStartup
+atToken
+beforeAtomic
+beforeBinary
+beforeClause
+beforeControl
+beforeDefined
+beforeEscape
+beforeEvaluate
+beforeExecute
+
+beforeExpand
+beforeFile
+beforeImport
+beforeInclude
+beforeLiteral
+beforeMulti
+beforeQuote
+beforeSerialize
+beforeSignificate
+beforeSingle
+beforeString
+deregister
+null
+pop
+push
+register
+
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + afterAtomic  +
+
+afterAtomic ( self )
+
+
+ + +
+ +   + + + afterBinary  +
+
+afterBinary ( self )
+
+
+ + +
+ +   + + + afterClause  +
+
+afterClause (
+        self,
+        exception,
+        variable,
+        )
+
+
+ + +
+ +   + + + afterControl  +
+
+afterControl ( self )
+
+
+ + +
+ +   + + + afterDefined  +
+
+afterDefined ( self,  result )
+
+
+ + +
+ +   + + + afterEscape  +
+
+afterEscape ( self,  result )
+
+
+ + +
+ +   + + + afterEvaluate  +
+
+afterEvaluate ( self,  result )
+
+
+ + +
+ +   + + + afterExecute  +
+
+afterExecute ( self )
+
+
+ + +
+ +   + + + afterExpand  +
+
+afterExpand ( self,  result )
+
+
+ + +
+ +   + + + afterFile  +
+
+afterFile ( self )
+
+
+ + +
+ +   + + + afterImport  +
+
+afterImport ( self )
+
+
+ + +
+ +   + + + afterInclude  +
+
+afterInclude ( self )
+
+
+ + +
+ +   + + + afterLiteral  +
+
+afterLiteral ( self )
+
+
+ + +
+ +   + + + afterMulti  +
+
+afterMulti ( self )
+
+
+ + +
+ +   + + + afterQuote  +
+
+afterQuote ( self,  result )
+
+
+ + +
+ +   + + + afterSerialize  +
+
+afterSerialize ( self )
+
+
+ + +
+ +   + + + afterSignificate  +
+
+afterSignificate ( self )
+
+
+ + +
+ +   + + + afterSingle  +
+
+afterSingle ( self )
+
+
+ + +
+ +   + + + afterString  +
+
+afterString ( self )
+
+
+ + +
+ +   + + + atFinalize  +
+
+atFinalize ( self )
+
+
+ + +
+ +   + + + atHandle  +
+
+atHandle ( self,  meta )
+
+
+ + +
+ +   + + + atInteract  +
+
+atInteract ( self )
+
+
+ + +
+ +   + + + atParse  +
+
+atParse (
+        self,
+        scanner,
+        locals,
+        )
+
+
+ + +
+ +   + + + atReady  +
+
+atReady ( self )
+
+
+ + +
+ +   + + + atShutdown  +
+
+atShutdown ( self )
+
+
+ + +
+ +   + + + atStartup  +
+
+atStartup ( self )
+
+
+ + +
+ +   + + + atToken  +
+
+atToken ( self,  token )
+
+
+ + +
+ +   + + + beforeAtomic  +
+
+beforeAtomic (
+        self,
+        name,
+        value,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeBinary  +
+
+beforeBinary (
+        self,
+        name,
+        file,
+        chunkSize,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeClause  +
+
+beforeClause (
+        self,
+        catch,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeControl  +
+
+beforeControl (
+        self,
+        type,
+        rest,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeDefined  +
+
+beforeDefined (
+        self,
+        name,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeEscape  +
+
+beforeEscape (
+        self,
+        string,
+        more,
+        )
+
+
+ + +
+ +   + + + beforeEvaluate  +
+
+beforeEvaluate (
+        self,
+        expression,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeExecute  +
+
+beforeExecute (
+        self,
+        statements,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeExpand  +
+
+beforeExpand (
+        self,
+        string,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeFile  +
+
+beforeFile (
+        self,
+        name,
+        file,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeImport  +
+
+beforeImport (
+        self,
+        name,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeInclude  +
+
+beforeInclude (
+        self,
+        name,
+        file,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeLiteral  +
+
+beforeLiteral ( self,  text )
+
+
+ + +
+ +   + + + beforeMulti  +
+
+beforeMulti (
+        self,
+        name,
+        values,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeQuote  +
+
+beforeQuote ( self,  string )
+
+
+ + +
+ +   + + + beforeSerialize  +
+
+beforeSerialize (
+        self,
+        expression,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeSignificate  +
+
+beforeSignificate (
+        self,
+        key,
+        value,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeSingle  +
+
+beforeSingle (
+        self,
+        source,
+        locals,
+        )
+
+
+ + +
+ +   + + + beforeString  +
+
+beforeString (
+        self,
+        name,
+        string,
+        locals,
+        )
+
+
+ + +
+ +   + + + deregister  +
+
+deregister ( self,  interpreter )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +Error, "hook not associated with this interpreter"
+ +
+ +
+ +   + + + null  +
+
+null ( self )
+
+
+ + +
+ +   + + + pop  +
+
+pop ( self )
+
+
+ + +
+ +   + + + push  +
+
+push ( self )
+
+
+ + +
+ +   + + + register  +
+
+register ( self,  interpreter )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/HookError.html b/doc/home/max/projects/empy/doc/em/HookError.html new file mode 100644 index 0000000..b18ad3c --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/HookError.html @@ -0,0 +1,83 @@ + + + + + + Class: HookError + + + + +

Table of Contents

+ + + + + + + + + +
Class: HookError + em.py +
+ +

An exception associated with hooks.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 00:58:36 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/InPlaceToken.html b/doc/home/max/projects/empy/doc/em/InPlaceToken.html new file mode 100644 index 0000000..ae748ba --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/InPlaceToken.html @@ -0,0 +1,196 @@ + + + + + + Class: InPlaceToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: InPlaceToken + em.py +
+ +

An in-place markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Interpreter.html b/doc/home/max/projects/empy/doc/em/Interpreter.html new file mode 100644 index 0000000..92503d4 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Interpreter.html @@ -0,0 +1,3167 @@ + + + + + + Class: Interpreter + + + + +

Table of Contents

+ + + + + + + + + +
Class: Interpreter + em.py +
+ +

An interpreter can process chunks of EmPy code.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + + +
+__del__
+__init__
+__repr__
+addHook
+areHooksEnabled
+assign
+atExit
+atomic
+attachFilter
+binary
+clause
+clear
+clearGlobals
+clearHooks
+close
+context
+createDiversion
+defined
+deregister
+deregisterCallback
+disableHooks
+enableHooks
+escape
+evaluate
+execute
+expand
+fail
+file
+finalize
+fix
+flatten
+flush
+getAllDiversions
+
+getCallback
+getCurrentDiversion
+getFilter
+getGlobals
+getHooks
+getPrefix
+handle
+identify
+import_
+include
+installProxy
+interact
+invoke
+invokeCallback
+invokeHook
+literal
+meta
+multi
+nullFilter
+ok
+parse
+playAllDiversions
+playDiversion
+pop
+popContext
+purgeAllDiversions
+purgeDiversion
+push
+pushContext
+quote
+ready
+register
+registerCallback
+
+removeHook
+replayAllDiversions
+replayDiversion
+reset
+resetFilter
+restore
+restoreGlobals
+retrieveDiversion
+safe
+save
+saveGlobals
+serialize
+setContextLine
+setContextName
+setFilter
+setGlobals
+setPrefix
+shutdown
+significate
+single
+startDiversion
+stopDiverting
+stream
+string
+tokenize
+unfix
+update
+updateGlobals
+wrap
+write
+writelines
+
+ + +
+ +   + + + __del__  +
+
+__del__ ( self )
+
+
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        output=None,
+        argv=None,
+        prefix=DEFAULT_PREFIX,
+        pseudo=None,
+        options=None,
+        globals=None,
+        hooks=None,
+        )
+
+
+ + +
+ +   + + + __repr__  +
+
+__repr__ ( self )
+
+
+ + +
+ +   + + + addHook  +
+
+addHook (
+        self,
+        hook,
+        prepend=False,
+        )
+
+
+ +

Add a new hook; optionally insert it rather than appending it.

+ +
+ +   + + + areHooksEnabled  +
+
+areHooksEnabled ( self )
+
+
+ +

Return whether or not hooks are presently enabled.

+ +
+ +   + + + assign  +
+
+assign (
+        self,
+        name,
+        value,
+        locals=None,
+        )
+
+
+ +

Do a potentially complex (including tuple unpacking) assignment.

+ +
+ +   + + + atExit  +
+
+atExit ( self,  callable )
+
+
+ +

Register a function to be called at exit.

+ +
+ +   + + + atomic  +
+
+atomic (
+        self,
+        name,
+        value,
+        locals=None,
+        )
+
+
+ +

Do an atomic assignment.

+ +
+ +   + + + attachFilter  +
+
+attachFilter ( self,  shortcut )
+
+
+ +

Attach a single filter to the end of the current filter chain.

+ +
+ +   + + + binary  +
+
+binary (
+        self,
+        file,
+        name='<binary>',
+        chunkSize=0,
+        locals=None,
+        )
+
+
+ +

Parse the entire contents of a file-like object, in chunks.

+ +
+ +   + + + clause  +
+
+clause (
+        self,
+        catch,
+        locals=None,
+        )
+
+
+ +

Given the string representation of an except clause, turn it into + a 2-tuple consisting of the class name, and either a variable name + or None.

+ +
+ +   + + + clear  +
+
+clear ( self )
+
+
+ +

Clear out the globals dictionary with a brand new one.

+ +
+ +   + + + clearGlobals  +
+
+clearGlobals ( self )
+
+
+ +

Clear out the globals with a brand new dictionary.

+ +
+ +   + + + clearHooks  +
+
+clearHooks ( self )
+
+
+ +

Clear all hooks.

+ +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + context  +
+
+context ( self )
+
+
+ + +
+ +   + + + createDiversion  +
+
+createDiversion ( self,  name )
+
+
+ +

Create a diversion (but do not divert to it) if it does not + already exist.

+ +
+ +   + + + defined  +
+
+defined (
+        self,
+        name,
+        locals=None,
+        )
+
+
+ +

Return a Boolean indicating whether or not the name is + defined either in the locals or the globals.

+ +
+ +   + + + deregister  +
+
+deregister ( self,  hook )
+
+
+ +

Remove an already registered hook.

+ +
+ +   + + + deregisterCallback  +
+
+deregisterCallback ( self )
+
+
+ +

Remove any previously registered callback with this interpreter.

+ +
+ +   + + + disableHooks  +
+
+disableHooks ( self )
+
+
+ +

Disable hooks.

+ +
+ +   + + + enableHooks  +
+
+enableHooks ( self )
+
+
+ +

Enable hooks.

+ +
+ +   + + + escape  +
+
+escape (
+        self,
+        data,
+        more='',
+        )
+
+
+ +

Escape a string so that nonprintable characters are replaced + with compatible EmPy expansions.

+ +
+ +   + + + evaluate  +
+
+evaluate (
+        self,
+        expression,
+        locals=None,
+        )
+
+
+ +

Evaluate an expression.

+ +
+ +   + + + execute  +
+
+execute (
+        self,
+        statements,
+        locals=None,
+        )
+
+
+ +

Execute a statement.

+ +
+ +   + + + expand  +
+
+expand (
+        self,
+        data,
+        locals=None,
+        )
+
+
+ +

Do an explicit expansion on a subordinate stream.

+ +
+ +   + + + fail  +
+
+fail (
+        self,
+        error,
+        fatal=False,
+        )
+
+
+ +

Handle an actual error that occurred.

+ +
+ +   + + + file  +
+
+file (
+        self,
+        file,
+        name='<file>',
+        locals=None,
+        )
+
+
+ +

Parse the entire contents of a file-like object, line by line.

+ +
+ +   + + + finalize  +
+
+finalize ( self )
+
+
+ +

Execute any remaining final routines.

+ +
+ +   + + + fix  +
+
+fix ( self )
+
+
+ +

Reset the globals, stamping in the pseudomodule.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +Error, "interpreter globals collision"
+ +
+ +
+ +   + + + flatten  +
+
+flatten ( self,  keys=None )
+
+
+ +

Flatten the contents of the pseudo-module into the globals + namespace.

+ +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + getAllDiversions  +
+
+getAllDiversions ( self )
+
+
+ +

Get the names of all existing diversions.

+ +
+ +   + + + getCallback  +
+
+getCallback ( self )
+
+
+ +

Get the callback registered with this interpreter, or None.

+ +
+ +   + + + getCurrentDiversion  +
+
+getCurrentDiversion ( self )
+
+
+ +

Get the name of the current diversion.

+ +
+ +   + + + getFilter  +
+
+getFilter ( self )
+
+
+ +

Get the current filter.

+ +
+ +   + + + getGlobals  +
+
+getGlobals ( self )
+
+
+ +

Retrieve the globals.

+ +
+ +   + + + getHooks  +
+
+getHooks ( self )
+
+
+ +

Get the current hooks.

+ +
+ +   + + + getPrefix  +
+
+getPrefix ( self )
+
+
+ +

Get the current prefix.

+ +
+ +   + + + handle  +
+
+handle ( self,  meta )
+
+
+ +

Handle a MetaError.

+ +
+ +   + + + identify  +
+
+identify ( self )
+
+
+ +

Identify the topmost context with a 2-tuple of the name and + line number.

+ +
+ +   + + + import_  +
+
+import_ (
+        self,
+        name,
+        locals=None,
+        )
+
+
+ +

Do an import.

+ +
+ +   + + + include  +
+
+include (
+        self,
+        fileOrFilename,
+        locals=None,
+        )
+
+
+ +

Do an include pass on a file or filename.

+ +
+ +   + + + installProxy  +
+
+installProxy ( self )
+
+
+ +

Install a proxy if necessary.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +Error, "interpreter stdout proxy lost"
+ +
+ +
+ +   + + + interact  +
+
+interact ( self )
+
+
+ +

Perform interaction.

+ +
+ +   + + + invoke  +
+
+invoke (
+        self,
+        _name,
+        **keywords,
+        )
+
+
+ +

Invoke the hook(s) associated with the hook name, should they + exist.

+ +
+ +   + + + invokeCallback  +
+
+invokeCallback ( self,  contents )
+
+
+ +

Invoke the callback.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +Error, "custom markup invoked with no defined callback"
+ +
+ +
+ +   + + + invokeHook  +
+
+invokeHook (
+        self,
+        _name,
+        **keywords,
+        )
+
+
+ +

Manually invoke a hook.

+ +
+ +   + + + literal  +
+
+literal ( self,  text )
+
+
+ +

Process a string literal.

+ +
+ +   + + + meta  +
+
+meta ( self,  exc=None )
+
+
+ +

Construct a MetaError for the interpreter's current state.

+ +
+ +   + + + multi  +
+
+multi (
+        self,
+        names,
+        values,
+        locals=None,
+        )
+
+
+ +

Do a (potentially recursive) assignment.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TypeError, "unpack non-sequence"
+ValueError, "unpack tuple of wrong size"
+ +
+ +
+ +   + + + nullFilter  +
+
+nullFilter ( self )
+
+
+ +

Install a filter that will consume all text.

+ +
+ +   + + + ok  +
+
+ok ( self )
+
+
+ +

Is the interpreter still active?

+ +
+ +   + + + parse  +
+
+parse (
+        self,
+        scanner,
+        locals=None,
+        )
+
+
+ +

Parse and run as much from this scanner as possible.

+ +
+ +   + + + playAllDiversions  +
+
+playAllDiversions ( self )
+
+
+ +

Play all existing diversions and then purge them.

+ +
+ +   + + + playDiversion  +
+
+playDiversion ( self,  name )
+
+
+ +

Play the given diversion and then purge it.

+ +
+ +   + + + pop  +
+
+pop ( self )
+
+
+ + +
+ +   + + + popContext  +
+
+popContext ( self )
+
+
+ +

Pop the top context.

+ +
+ +   + + + purgeAllDiversions  +
+
+purgeAllDiversions ( self )
+
+
+ +

Purge all existing diversions.

+ +
+ +   + + + purgeDiversion  +
+
+purgeDiversion ( self,  name )
+
+
+ +

Eliminate the given diversion.

+ +
+ +   + + + push  +
+
+push ( self )
+
+
+ + +
+ +   + + + pushContext  +
+
+pushContext (
+        self,
+        name='<unnamed>',
+        line=0,
+        )
+
+
+ +

Create a new context and push it.

+ +
+ +   + + + quote  +
+
+quote ( self,  data )
+
+
+ +

Quote the given string so that if it were expanded it would + evaluate to the original.

+ +
+ +   + + + ready  +
+
+ready ( self )
+
+
+ +

Declare the interpreter ready for normal operations.

+ +
+ +   + + + register  +
+
+register (
+        self,
+        hook,
+        prepend=False,
+        )
+
+
+ +

Register the provided hook.

+ +
+ +   + + + registerCallback  +
+
+registerCallback ( self,  callback )
+
+
+ +

Register a custom markup callback with this interpreter.

+ +
+ +   + + + removeHook  +
+
+removeHook ( self,  hook )
+
+
+ +

Remove a preexisting hook.

+ +
+ +   + + + replayAllDiversions  +
+
+replayAllDiversions ( self )
+
+
+ +

Replay all existing diversions without purging them.

+ +
+ +   + + + replayDiversion  +
+
+replayDiversion ( self,  name )
+
+
+ +

Replay the diversion without purging it.

+ +
+ +   + + + reset  +
+
+reset ( self )
+
+
+ + +
+ +   + + + resetFilter  +
+
+resetFilter ( self )
+
+
+ +

Reset the filter so that it does no filtering.

+ +
+ +   + + + restore  +
+
+restore ( self,  destructive=True )
+
+
+ +

Restore the topmost historic globals.

+ +
+ +   + + + restoreGlobals  +
+
+restoreGlobals ( self,  destructive=True )
+
+
+ +

Restore the most recently saved copy of the globals.

+ +
+ +   + + + retrieveDiversion  +
+
+retrieveDiversion ( self,  name )
+
+
+ +

Retrieve the diversion object associated with the name.

+ +
+ +   + + + safe  +
+
+safe (
+        self,
+        scanner,
+        final=False,
+        locals=None,
+        )
+
+
+ +

Do a protected parse. Catch transient parse errors; if + final is true, then make a final pass with a terminator, + otherwise ignore the transient parse error (more data is + pending).

+ +
+ +   + + + save  +
+
+save ( self,  deep=True )
+
+
+ + +
+ +   + + + saveGlobals  +
+
+saveGlobals ( self,  deep=True )
+
+
+ +

Save a copy of the globals off onto the history stack.

+ +
+ +   + + + serialize  +
+
+serialize (
+        self,
+        expression,
+        locals=None,
+        )
+
+
+ +

Do an expansion, involving evaluating an expression, then + converting it to a string and writing that string to the + output if the evaluation is not None.

+ +
+ +   + + + setContextLine  +
+
+setContextLine ( self,  line )
+
+
+ +

Set the name of the topmost context.

+ +
+ +   + + + setContextName  +
+
+setContextName ( self,  name )
+
+
+ +

Set the name of the topmost context.

+ +
+ +   + + + setFilter  +
+
+setFilter ( self,  shortcut )
+
+
+ +

Set the filter.

+ +
+ +   + + + setGlobals  +
+
+setGlobals ( self,  globals )
+
+
+ +

Set the globals to the specified dictionary.

+ +
+ +   + + + setPrefix  +
+
+setPrefix ( self,  prefix )
+
+
+ +

Set the prefix.

+ +
+ +   + + + shutdown  +
+
+shutdown ( self )
+
+
+ +

Declare this interpreting session over; close the stream file + object. This method is idempotent.

+ +
+ +   + + + significate  +
+
+significate (
+        self,
+        key,
+        value=None,
+        locals=None,
+        )
+
+
+ +

Declare a significator.

+ +
+ +   + + + single  +
+
+single (
+        self,
+        source,
+        locals=None,
+        )
+
+
+ +

Execute an expression or statement, just as if it were + entered into the Python interactive interpreter.

+ +
+ +   + + + startDiversion  +
+
+startDiversion ( self,  name )
+
+
+ +

Start diverting to the given diversion name.

+ +
+ +   + + + stopDiverting  +
+
+stopDiverting ( self )
+
+
+ +

Stop any diverting.

+ +
+ +   + + + stream  +
+
+stream ( self )
+
+
+ + +
+ +   + + + string  +
+
+string (
+        self,
+        data,
+        name='<string>',
+        locals=None,
+        )
+
+
+ +

Parse a string.

+ +
+ +   + + + tokenize  +
+
+tokenize ( self,  name )
+
+
+ +

Take an lvalue string and return a name or a (possibly recursive) + list of names.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "unexpected assignment token: '%s'" % garbage
+ +
+ +
+ +   + + + unfix  +
+
+unfix ( self )
+
+
+ +

Remove the pseudomodule (if present) from the globals.

+ +
+ +   + + + update  +
+
+update ( self,  other )
+
+
+ +

Update the current globals dictionary with another dictionary.

+ +
+ +   + + + updateGlobals  +
+
+updateGlobals ( self,  otherGlobals )
+
+
+ +

Merge another mapping object into this interpreter's globals.

+ +
+ +   + + + wrap  +
+
+wrap (
+        self,
+        callable,
+        args,
+        )
+
+
+ +

Wrap around an application of a callable and handle errors. + Return whether no error occurred.

+ +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  stuff )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/LineBufferedFilter.html b/doc/home/max/projects/empy/doc/em/LineBufferedFilter.html new file mode 100644 index 0000000..a4ff9f9 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/LineBufferedFilter.html @@ -0,0 +1,165 @@ + + + + + + Class: LineBufferedFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: LineBufferedFilter + em.py +
+ +

A line-buffered filter only lets data through when it sees + whole lines.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +BufferedFilter
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+write
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/LiteralToken.html b/doc/home/max/projects/empy/doc/em/LiteralToken.html new file mode 100644 index 0000000..e48147e --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/LiteralToken.html @@ -0,0 +1,168 @@ + + + + + + Class: LiteralToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: LiteralToken + em.py +
+ +

A literal markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/MaximallyBufferedFilter.html b/doc/home/max/projects/empy/doc/em/MaximallyBufferedFilter.html new file mode 100644 index 0000000..1795b4e --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/MaximallyBufferedFilter.html @@ -0,0 +1,193 @@ + + + + + + Class: MaximallyBufferedFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: MaximallyBufferedFilter + em.py +
+ +

A maximally-buffered filter only lets its data through on the final + close. It ignores flushes.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +BufferedFilter
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+close
+flush
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/MetaError.html b/doc/home/max/projects/empy/doc/em/MetaError.html new file mode 100644 index 0000000..039eb1b --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/MetaError.html @@ -0,0 +1,164 @@ + + + + + + Class: MetaError + + + + +

Table of Contents

+ + + + + + + + + +
Class: MetaError + em.py +
+ +

A wrapper around a real Python exception for including a copy of + the context.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Exception
+ +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+__str__
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        contexts,
+        exc,
+        )
+
+
+ + +
+ +   + + + __str__  +
+
+__str__ ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/NullFile.html b/doc/home/max/projects/empy/doc/em/NullFile.html new file mode 100644 index 0000000..6f5178c --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/NullFile.html @@ -0,0 +1,216 @@ + + + + + + Class: NullFile + + + + +

Table of Contents

+ + + + + + + + + +
Class: NullFile + em.py +
+ +

A simple class that supports all the file-like object methods + but simply does nothing at all.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+close
+flush
+write
+writelines
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/NullFilter.html b/doc/home/max/projects/empy/doc/em/NullFilter.html new file mode 100644 index 0000000..6cd75d0 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/NullFilter.html @@ -0,0 +1,131 @@ + + + + + + Class: NullFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: NullFilter + em.py +
+ +

A filter that never sends any output to its sink.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Filter
+ +
+ + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +write
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/NullToken.html b/doc/home/max/projects/empy/doc/em/NullToken.html new file mode 100644 index 0000000..7cc817a --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/NullToken.html @@ -0,0 +1,191 @@ + + + + + + Class: NullToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: NullToken + em.py +
+ +

A chunk of data not containing markups.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Token
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+run
+string
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  data )
+
+
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ParseError.html b/doc/home/max/projects/empy/doc/em/ParseError.html new file mode 100644 index 0000000..6bc26ff --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ParseError.html @@ -0,0 +1,83 @@ + + + + + + Class: ParseError + + + + +

Table of Contents

+ + + + + + + + + +
Class: ParseError + em.py +
+ +

A parse error occurred.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/PrefixToken.html b/doc/home/max/projects/empy/doc/em/PrefixToken.html new file mode 100644 index 0000000..7791691 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/PrefixToken.html @@ -0,0 +1,168 @@ + + + + + + Class: PrefixToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: PrefixToken + em.py +
+ +

A prefix markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Processor.html b/doc/home/max/projects/empy/doc/em/Processor.html new file mode 100644 index 0000000..c660ed1 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Processor.html @@ -0,0 +1,323 @@ + + + + + + Class: Processor + + + + +

Table of Contents

+ + + + + + + + + +
Class: Processor + em.py +
+ +

An entity which is capable of processing a hierarchy of EmPy + files and building a dictionary of document objects associated + with them describing their significator contents.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+clear
+directory
+file
+identifier
+line
+postprocess
+scan
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  factory=Document )
+
+
+ + +
+ +   + + + clear  +
+
+clear ( self )
+
+
+ + +
+ +   + + + directory  +
+
+directory (
+        self,
+        basename,
+        dirCriteria,
+        fileCriteria,
+        depth=None,
+        )
+
+
+ + +
+ +   + + + file  +
+
+file (
+        self,
+        document,
+        file,
+        )
+
+
+ + +
+ +   + + + identifier  +
+
+identifier (
+        self,
+        pathname,
+        filename,
+        )
+
+
+ + +
+ +   + + + line  +
+
+line (
+        self,
+        document,
+        line,
+        )
+
+
+ + +
+ +   + + + postprocess  +
+
+postprocess ( self )
+
+
+ + +
+ +   + + + scan  +
+
+scan (
+        self,
+        basename,
+        extensions=DEFAULT_EMPY_EXTENSIONS,
+        )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ProxyFile.html b/doc/home/max/projects/empy/doc/em/ProxyFile.html new file mode 100644 index 0000000..22b85f2 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ProxyFile.html @@ -0,0 +1,369 @@ + + + + + + Class: ProxyFile + + + + +

Table of Contents

+ + + + + + + + + +
Class: ProxyFile + em.py +
+ +

The proxy file object that is intended to take the place of + sys.stdout. The proxy can manage a stack of file objects it is + writing to, and an underlying raw file object.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + +
+__init__
+_testProxy
+clear
+close
+current
+
+flush
+pop
+push
+write
+writelines
+
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  bottom )
+
+
+ + +
+ +   + + + _testProxy  +
+
+_testProxy ( self )
+
+
+ + +
+ +   + + + clear  +
+
+clear ( self,  interpreter )
+
+
+ + +
+ +   + + + close  +
+
+close ( self )
+
+
+ +

Close the current file. If the current file is the bottom, then + close it and dispose of it.

+ +
+ +   + + + current  +
+
+current ( self )
+
+
+ +

Get the current stream to write to.

+ +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + pop  +
+
+pop ( self,  interpreter )
+
+
+ + +
+ +   + + + push  +
+
+push ( self,  interpreter )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/ReprToken.html b/doc/home/max/projects/empy/doc/em/ReprToken.html new file mode 100644 index 0000000..a1adc22 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/ReprToken.html @@ -0,0 +1,196 @@ + + + + + + Class: ReprToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: ReprToken + em.py +
+ +

A repr markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Scanner.html b/doc/home/max/projects/empy/doc/em/Scanner.html new file mode 100644 index 0000000..ddc404c --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Scanner.html @@ -0,0 +1,1267 @@ + + + + + + Class: Scanner + + + + +

Table of Contents

+ + + + + + + + + +
Class: Scanner + em.py +
+ +

A scanner holds a buffer for lookahead parsing and has the + ability to scan for special symbols and indicators in that + buffer.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + + +
+__getitem__
+__getslice__
+__init__
+__len__
+__nonzero__
+acquire
+advance
+check
+chop
+
+complex
+feed
+find
+last
+nested
+next
+one
+phrase
+quote
+
+read
+release
+rest
+retreat
+set
+simple
+sync
+unsync
+word
+
+ + +
+ +   + + + __getitem__  +
+
+__getitem__ ( self,  index )
+
+
+ + +
+ +   + + + __getslice__  +
+
+__getslice__ (
+        self,
+        start,
+        stop,
+        )
+
+
+ + +
+ +   + + + __init__  +
+
+__init__ (
+        self,
+        prefix,
+        data='',
+        )
+
+
+ + +
+ +   + + + __len__  +
+
+__len__ ( self )
+
+
+ + +
+ +   + + + __nonzero__  +
+
+__nonzero__ ( self )
+
+
+ + +
+ +   + + + acquire  +
+
+acquire ( self )
+
+
+ +

Lock the scanner so it doesn't destroy data on sync.

+ +
+ +   + + + advance  +
+
+advance ( self,  count=1 )
+
+
+ +

Advance the pointer count characters.

+ +
+ +   + + + check  +
+
+check (
+        self,
+        i,
+        archetype=None,
+        )
+
+
+ +

Scan for the next single or triple quote, with the specified + archetype. Return the found quote or None.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "need to scan for rest of quote"
+ +
+ +
+ +   + + + chop  +
+
+chop (
+        self,
+        count=None,
+        slop=0,
+        )
+
+
+ +

Chop the first count + slop characters off the front, and return + the first count. If count is not specified, then return + everything.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "not enough data to read"
+ +
+ +
+ +   + + + complex  +
+
+complex (
+        self,
+        enter,
+        exit,
+        start=0,
+        end=None,
+        skip=None,
+        )
+
+
+ +

Scan from i for an ending sequence, respecting quotes, + entries and exits.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "expecting end of complex expression"
+ +
+ +
+ +   + + + feed  +
+
+feed ( self,  data )
+
+
+ +

Feed some more data to the scanner.

+ +
+ +   + + + find  +
+
+find (
+        self,
+        sub,
+        start=0,
+        end=None,
+        )
+
+
+ +

Find the next occurrence of the character, or return -1.

+ +
+ +   + + + last  +
+
+last (
+        self,
+        char,
+        start=0,
+        end=None,
+        )
+
+
+ +

Find the first character that is not the specified character.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "expecting other than %s" % char
+ +
+ +
+ +   + + + nested  +
+
+nested (
+        self,
+        enter,
+        exit,
+        start=0,
+        end=None,
+        )
+
+
+ +

Scan from i for an ending sequence, respecting entries and exits + only.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "expecting end of complex expression"
+ +
+ +
+ +   + + + next  +
+
+next (
+        self,
+        target,
+        start=0,
+        end=None,
+        mandatory=False,
+        )
+
+
+ +

Scan for the next occurrence of one of the characters in + the target string; optionally, make the scan mandatory.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "expecting %s, not found" % target
+TransientParseError, "expecting ending character"
+ +
+ +
+ +   + + + one  +
+
+one ( self )
+
+
+ +

Parse and return one token, or None if the scanner is empty.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "unknown markup: %s%s" %( self.prefix, first )
+ +
+ +
+ +   + + + phrase  +
+
+phrase ( self,  start=0 )
+
+
+ +

Scan from i for a phrase (e.g., word, f(a, b, c), 'a[i]', or + combinations like 'x[i](a)'.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "curly braces can't open simple expressions"
+ +
+ +
+ +   + + + quote  +
+
+quote (
+        self,
+        start=0,
+        end=None,
+        mandatory=False,
+        )
+
+
+ +

Scan for the end of the next quote.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "expecting end of string literal"
+TransientParseError, "expecting end of string literal"
+ +
+ +
+ +   + + + read  +
+
+read (
+        self,
+        i=0,
+        count=1,
+        )
+
+
+ +

Read count chars starting from i; raise a transient error if + there aren't enough characters remaining.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "need more data to read"
+ +
+ +
+ +   + + + release  +
+
+release ( self )
+
+
+ +

Unlock the scanner.

+ +
+ +   + + + rest  +
+
+rest ( self )
+
+
+ +

Get the remainder of the buffer.

+ +
+ +   + + + retreat  +
+
+retreat ( self,  count=1 )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "can't retreat back over synced out chars"
+ +
+ +
+ +   + + + set  +
+
+set ( self,  data )
+
+
+ +

Start the scanner digesting a new batch of data; start the pointer + over from scratch.

+ +
+ +   + + + simple  +
+
+simple ( self,  start=0 )
+
+
+ +

Scan from i for a simple expression, which consists of one + more phrases separated by dots.

+ +
+ +   + + + sync  +
+
+sync ( self )
+
+
+ +

Sync up the buffer with the read head.

+ +
+ +   + + + unsync  +
+
+unsync ( self )
+
+
+ +

Undo changes; reset the read head.

+ +
+ +   + + + word  +
+
+word ( self,  start=0 )
+
+
+ +

Scan from i for a simple word.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +TransientParseError, "expecting end of word"
+ +
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/SignificatorToken.html b/doc/home/max/projects/empy/doc/em/SignificatorToken.html new file mode 100644 index 0000000..36724a0 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/SignificatorToken.html @@ -0,0 +1,226 @@ + + + + + + Class: SignificatorToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: SignificatorToken + em.py +
+ +

A significator markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +ParseError, "no whitespace between % and key"
+ParseError, "significator must have nonblank key"
+TransientParseError, "significator expects newline"
+ +
+ +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/SimpleExpressionToken.html b/doc/home/max/projects/empy/doc/em/SimpleExpressionToken.html new file mode 100644 index 0000000..3d71d27 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/SimpleExpressionToken.html @@ -0,0 +1,196 @@ + + + + + + Class: SimpleExpressionToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: SimpleExpressionToken + em.py +
+ +

A simple expression markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/SizeBufferedFilter.html b/doc/home/max/projects/empy/doc/em/SizeBufferedFilter.html new file mode 100644 index 0000000..2e187ae --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/SizeBufferedFilter.html @@ -0,0 +1,165 @@ + + + + + + Class: SizeBufferedFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: SizeBufferedFilter + em.py +
+ +

A size-buffered filter only in fixed size chunks (excepting the + final chunk).

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +BufferedFilter
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+write
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  bufferSize )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Stack.html b/doc/home/max/projects/empy/doc/em/Stack.html new file mode 100644 index 0000000..b97e342 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Stack.html @@ -0,0 +1,455 @@ + + + + + + Class: Stack + + + + +

Table of Contents

+ + + + + + + + + +
Class: Stack + em.py +
+ +

A simple stack that behaves as a sequence (with 0 being the top + of the stack, not the bottom).

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + +
+__getitem__
+__init__
+__len__
+__nonzero__
+__repr__
+clone
+
+filter
+pop
+purge
+push
+top
+
+ + +
+ +   + + + __getitem__  +
+
+__getitem__ ( self,  index )
+
+
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  seq=None )
+
+
+ + +
+ +   + + + __len__  +
+
+__len__ ( self )
+
+
+ + +
+ +   + + + __nonzero__  +
+
+__nonzero__ ( self )
+
+
+ + +
+ +   + + + __repr__  +
+
+__repr__ ( self )
+
+
+ + +
+ +   + + + clone  +
+
+clone ( self )
+
+
+ +

Create a duplicate of this stack.

+ +
+ +   + + + filter  +
+
+filter ( self,  function )
+
+
+ +

Filter the elements of the stack through the function.

+ +
+ +   + + + pop  +
+
+pop ( self )
+
+
+ +

Pop the top element off the stack and return it.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +StackUnderflowError, "stack is empty for pop"
+ +
+ +
+ +   + + + purge  +
+
+purge ( self )
+
+
+ +

Purge the stack.

+ +
+ +   + + + push  +
+
+push ( self,  object )
+
+
+ +

Push an element onto the top of the stack.

+ +
+ +   + + + top  +
+
+top ( self )
+
+
+ +

Access the top element on the stack.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +StackUnderflowError, "stack is empty for top"
+ +
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/StackUnderflowError.html b/doc/home/max/projects/empy/doc/em/StackUnderflowError.html new file mode 100644 index 0000000..741d38e --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/StackUnderflowError.html @@ -0,0 +1,83 @@ + + + + + + Class: StackUnderflowError + + + + +

Table of Contents

+ + + + + + + + + +
Class: StackUnderflowError + em.py +
+ +

A stack underflow.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/StatementToken.html b/doc/home/max/projects/empy/doc/em/StatementToken.html new file mode 100644 index 0000000..d0cf55b --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/StatementToken.html @@ -0,0 +1,196 @@ + + + + + + Class: StatementToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: StatementToken + em.py +
+ +

A statement markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Stream.html b/doc/home/max/projects/empy/doc/em/Stream.html new file mode 100644 index 0000000..448ba19 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Stream.html @@ -0,0 +1,753 @@ + + + + + + Class: Stream + + + + +

Table of Contents

+ + + + + + + + + +
Class: Stream + em.py +
+ +

A wrapper around an (output) file object which supports + diversions and filtering.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ + + + + + + +
+__init__
+attach
+close
+create
+divert
+flush
+install
+last
+purge
+
+purgeAll
+retrieve
+revert
+shortcut
+undivert
+undivertAll
+write
+writelines
+
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  file )
+
+
+ + +
+ +   + + + attach  +
+
+attach ( self,  shortcut )
+
+
+ +

Attached a solitary filter (no sequences allowed here) at the + end of the current filter chain.

+ +
+ +   + + + close  +
+
+close ( self )
+
+
+ + +
+ +   + + + create  +
+
+create ( self,  name )
+
+
+ +

Create a diversion if one does not already exist, but do not + divert to it yet.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +DiversionError, "diversion name must be non-None"
+ +
+ +
+ +   + + + divert  +
+
+divert ( self,  name )
+
+
+ +

Start diverting.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +DiversionError, "diversion name must be non-None"
+ +
+ +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + install  +
+
+install ( self,  shortcut=None )
+
+
+ +

Install a new filter; None means no filter. Handle all the + special shortcuts for filters here.

+ +
+ +   + + + last  +
+
+last ( self )
+
+
+ +

Find the last filter in the current filter chain, or None if + there are no filters installed.

+ +
+ +   + + + purge  +
+
+purge ( self,  name )
+
+
+ +

Purge the specified diversion.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +DiversionError, "diversion name must be non-None"
+ +
+ +
+ +   + + + purgeAll  +
+
+purgeAll ( self )
+
+
+ +

Eliminate all existing diversions.

+ +
+ +   + + + retrieve  +
+
+retrieve ( self,  name )
+
+
+ +

Retrieve the given diversion.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +DiversionError, "diversion name must be non-None"
+DiversionError, "nonexistent diversion: %s" % name
+ +
+ +
+ +   + + + revert  +
+
+revert ( self )
+
+
+ +

Reset any current diversions.

+ +
+ +   + + + shortcut  +
+
+shortcut ( self,  shortcut )
+
+
+ +

Take a filter shortcut and translate it into a filter, returning + it. Sequences don't count here; these should be detected + independently.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +NotImplementedError, "mapping filters not yet supported"
+ +
+ +
+ +   + + + undivert  +
+
+undivert (
+        self,
+        name,
+        purgeAfterwards=False,
+        )
+
+
+ +

Undivert a particular diversion.

+ + + + + + + + +
+ + Exceptions  + + +   +
+ +DiversionError, "diversion name must be non-None"
+DiversionError, "nonexistent diversion: %s" % name
+ +
+ +
+ +   + + + undivertAll  +
+
+undivertAll ( self,  purgeAfterwards=True )
+
+
+ +

Undivert all pending diversions.

+ +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/StringFilter.html b/doc/home/max/projects/empy/doc/em/StringFilter.html new file mode 100644 index 0000000..f665354 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/StringFilter.html @@ -0,0 +1,188 @@ + + + + + + Class: StringFilter + + + + +

Table of Contents

+ + + + + + + + + +
Class: StringFilter + em.py +
+ +

A filter that takes a translation string (256 characters) and + filters any incoming data through it.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Filter
+ +
+ + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+write
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  table )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +FilterError, "table must be 256-character string"
+ +
+ +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/StringLiteralToken.html b/doc/home/max/projects/empy/doc/em/StringLiteralToken.html new file mode 100644 index 0000000..4b18339 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/StringLiteralToken.html @@ -0,0 +1,196 @@ + + + + + + Class: StringLiteralToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: StringLiteralToken + em.py +
+ +

A string token markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +run
+scan
+string
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + +
+ +   + + + scan  +
+
+scan ( self,  scanner )
+
+
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Subsystem.html b/doc/home/max/projects/empy/doc/em/Subsystem.html new file mode 100644 index 0000000..c3eed0a --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Subsystem.html @@ -0,0 +1,318 @@ + + + + + + Class: Subsystem + + + + +

Table of Contents

+ + + + + + + + + +
Class: Subsystem + em.py +
+ +

The subsystem class defers file creation so that it can create + Unicode-wrapped files if desired (and possible).

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+assertUnicode
+defaultOpen
+initialize
+open
+unicodeOpen
+ + +
+ +   + + + __init__  +
+
+__init__ ( self )
+
+
+ + +
+ +   + + + assertUnicode  +
+
+assertUnicode ( self )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +SubsystemError, "Unicode subsystem unavailable"
+ +
+ +
+ +   + + + defaultOpen  +
+
+defaultOpen (
+        self,
+        name,
+        mode=None,
+        )
+
+
+ + +
+ +   + + + initialize  +
+
+initialize (
+        self,
+        inputEncoding=None,
+        outputEncoding=None,
+        inputErrors=None,
+        outputErrors=None,
+        )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +SubsystemError, "Unicode subsystem unavailable"
+ +
+ +
+ +   + + + open  +
+
+open (
+        self,
+        name,
+        mode=None,
+        )
+
+
+ + +
+ +   + + + unicodeOpen  +
+
+unicodeOpen (
+        self,
+        name,
+        mode=None,
+        )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/SubsystemError.html b/doc/home/max/projects/empy/doc/em/SubsystemError.html new file mode 100644 index 0000000..42793ed --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/SubsystemError.html @@ -0,0 +1,83 @@ + + + + + + Class: SubsystemError + + + + +

Table of Contents

+ + + + + + + + + +
Class: SubsystemError + em.py +
+ +

An error associated with the Unicode subsystem.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Error
+
    + +Exception
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/Token.html b/doc/home/max/projects/empy/doc/em/Token.html new file mode 100644 index 0000000..be2285e --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/Token.html @@ -0,0 +1,219 @@ + + + + + + Class: Token + + + + +

Table of Contents

+ + + + + + + + + +
Class: Token + em.py +
+ +

An element of expansion.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__str__
+run
+string
+ + +
+ +   + + + __str__  +
+
+__str__ ( self )
+
+
+ + +
+ +   + + + run  +
+
+run (
+        self,
+        interpreter,
+        locals,
+        )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +NotImplementedError
+ +
+ +
+ +   + + + string  +
+
+string ( self )
+
+
+ + + + + + + + + +
+ + Exceptions  + + +   +
+ +NotImplementedError
+ +
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/TransientParseError.html b/doc/home/max/projects/empy/doc/em/TransientParseError.html new file mode 100644 index 0000000..6ff7b61 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/TransientParseError.html @@ -0,0 +1,89 @@ + + + + + + Class: TransientParseError + + + + +

Table of Contents

+ + + + + + + + + +
Class: TransientParseError + em.py +
+ +

A parse error occurred which may be resolved by feeding more data. + Such an error reaching the toplevel is an unexpected EOF error.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ParseError
+
    + +Error
    +
      + +Exception
      + +
    + +
+ +
+ + +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/UncloseableFile.html b/doc/home/max/projects/empy/doc/em/UncloseableFile.html new file mode 100644 index 0000000..2d80112 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/UncloseableFile.html @@ -0,0 +1,217 @@ + + + + + + Class: UncloseableFile + + + + +

Table of Contents

+ + + + + + + + + +
Class: UncloseableFile + em.py +
+ +

A simple class which wraps around a delegate file-like object + and lets everything through except close calls.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+close
+flush
+write
+writelines
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  delegate )
+
+
+ + +
+ +   + + + close  +
+
+close ( self )
+
+
+ +

Eat this one.

+ +
+ +   + + + flush  +
+
+flush ( self )
+
+
+ + +
+ +   + + + write  +
+
+write ( self,  data )
+
+
+ + +
+ +   + + + writelines  +
+
+writelines ( self,  lines )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/VerboseHook.html b/doc/home/max/projects/empy/doc/em/VerboseHook.html new file mode 100644 index 0000000..f8062ab --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/VerboseHook.html @@ -0,0 +1,133 @@ + + + + + + Class: VerboseHook + + + + +

Table of Contents

+ + + + + + + + + +
Class: VerboseHook + em.py +
+ +

A verbose hook that reports all information received by the + hook interface. This class dynamically scans the Hook base class + to ensure that all hook methods are properly represented.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +Hook
+ +
+ + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +__init__
+ + +
+ +   + + + __init__  +
+
+__init__ ( self,  output=sys.stderr )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/home/max/projects/empy/doc/em/WhitespaceToken.html b/doc/home/max/projects/empy/doc/em/WhitespaceToken.html new file mode 100644 index 0000000..648fb97 --- /dev/null +++ b/doc/home/max/projects/empy/doc/em/WhitespaceToken.html @@ -0,0 +1,136 @@ + + + + + + Class: WhitespaceToken + + + + +

Table of Contents

+ + + + + + + + + +
Class: WhitespaceToken + em.py +
+ +

A whitespace markup.

+ + + + + + + + +
+ + Base Classes  + + +   +
+ +ExpansionToken
+ + +
+ + + + + + + + + + + + + + +
+ + Methods  + + +   +
+ +string
+ + +
+ +   + + + string  +
+
+string ( self )
+
+
+ +
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Mon Oct 27 01:29:13 2003 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..18471f2 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,2247 @@ + + + + + + empy + + + + +

Table of Contents

+ + + + + + + + + +
empy +   +
+ +

Summary

+

A powerful and robust templating system for Python.

+

Overview

+

EmPy is a system for embedding Python expressions and statements + in template text; it takes an EmPy source file, processes it, and + produces output. This is accomplished via expansions, which are + special signals to the EmPy system and are set off by a special + prefix (by default the at sign, @). EmPy can expand arbitrary + Python expressions and statements in this way, as well as a + variety of special forms. Textual data not explicitly delimited + in this way is sent unaffected to the output, allowing Python to + be used in effect as a markup language. Also supported are + callbacks via hooks, recording and playback via diversions, and + dynamic, chainable filters. The system is highly configurable via + command line options and embedded commands.

+

Expressions are embedded in text with the @(...) notation; + variations include conditional expressions with @(...?...!...) + and the ability to handle thrown exceptions with @(...$...). As + a shortcut, simple variables and expressions can be abbreviated as + @variable, @object.attribute, @function(arguments), + @sequence [index], and combinations. Full-fledged statements + are embedded with @{...}. Control flow in terms of conditional + or repeated expansion is available with @[...]. A @ followed + by a whitespace character (including a newline) expands to + nothing, allowing string concatenations and line continuations. + Comments are indicated with @# and consume the rest of the line, + up to and including the trailing newline. @% indicate + "significators," which are special forms of variable assignment + intended to specify per-file identification information in a + format which is easy to parse externally. Context name and line + number changes can be done with @? and @! respectively. + '@<...>' markups are customizeable by the user and can be used for + any desired purpose. Escape sequences analogous to those in C can + be specified with @\..., and finally a @@ sequence expands to + a single literal at sign.

+

Getting the software

+

The current version of empy is 3.3.2.

+

The latest version of the software is available in a tarball here: + http://www.alcyone.com/software/empy/empy-latest.tar.gz.

+

The official URL for this Web site is + http://www.alcyone.com/software/empy/.

+

Requirements

+

EmPy should work with any version of Python from 2.4 onward, + including 3.x.

+

License

+

This code is released under the LGPL.

+

Mailing lists

+

There are two EmPy related mailing lists available. The first is + a receive-only, very low volume list for important announcements + (including releases). To subscribe, send an email to + empy-announce-list-subscribe@alcyone.com.

+

The second is a general discussion list for topics related to + EmPy, and is open for everyone to contribute; announcements + related to EmPy will also be made on this list. The author of + EmPy (and any future developers) will also be on the list, so it + can be used not only to discuss EmPy features with other users, + but also to ask questions of the author(s). To subscribe, send an + email to empy-list-subscribe@alcyone.com.

+

Basics

+

EmPy is intended for embedding Python code in otherwise + unprocessed text. Source files are processed, and the results are + written to an output file. Normal text is sent to the output + unchanged, but markups are processed, expanded to their results, + and then written to the output file as strings (that is, with the + str function, not repr). The act of processing EmPy source + and handling markups is called "expansion."

+

Code that is processed is executed exactly as if it were entered + into the Python interpreter; that is, it is executed with the + equivalent of eval (for expressions) and exec (for + statements). EmPy is intended to be a very thin (though powerful) + layer on top of a running Python system; Python and EmPy files can + be mixed together (via command line options) without + complications.

+

By default the embedding prefix is the at sign (@), which + appears neither in valid Python code nor commonly in arbitrary + texts; it can be overridden with the -p option (or with the + empy.setPrefix function). The prefix indicates to the EmPy + interpreter that a special sequence follows and should be + processed rather than sent to the output untouched (to indicate a + literal at sign, it can be doubled as in @@).

+

When the interpreter starts processing its target file, no modules + are imported by default, save the empy pseudomodule (see below), + which is placed in the globals; the empy pseudomodule is + associated with a particular interpreter -- in fact, they are the + same object -- and it is important that it not be removed from + that interpreter's globals, nor that it be shared with other + interpreters running concurrently (a name other than empy can be + specified with the -m option). The globals are not cleared or + reset in any way. It is perfectly legal to set variables or + explicitly import modules and then use them in later markups, + e.g., @{import time} ... @time.time(). Scoping rules are as + in normal Python, although all defined variables and objects are + taken to be in the global namespace.

+

Indentation is significant in Python, and therefore is also + significant in EmPy. EmPy statement markups (@{...}), when + spanning multiple lines, must be flush with the left margin. This + is because (multiline) statement markups are not treated specially + in EmPy and are simply passed to the Python interpreter, where + indentation is significant.

+

Activities you would like to be done before any processing of the + main EmPy file can be specified with the -I, -D, -E, -F, and -P + options. -I imports modules, -D executes a Python variable + assignment, -E executes an arbitrary Python (not EmPy) statement, + -F executes a Python (not EmPy) file, and -P processes an EmPy + (not Python) file. These operations are done in the order they + appear on the command line; any number of each (including, of + course, zero) can be used.

+

Expansions

+

The following markups are supported. For concreteness below, @ + is taken for the sake of argument to be the prefix character, + although this can be changed.

+
+
@# COMMENT NEWLINE
+
A comment. Comments, including the + trailing newline, are stripped out completely. Comments should + only be present outside of expansions. The comment itself is + not processed in any way: It is completely discarded. This + allows @# comments to be used to disable markups. Note: As + special support for "bangpaths" in Unix-like operating systems, + if the first line of a file (or indeed any context) begins with + #!, and the interpreter has a processBangpaths option set to + true (default), it is treated as a @# comment. A #! + sequence appearing anywhere else will be handled literally and + unaltered in the expansion. Example: +
+          @# This line is a comment.
+          @# This will NOT be expanded: @x.
+
+
+
@? NAME NEWLINE
+
Set the name of the current context to be + the given string. Variables are not allowed here; the name is + treated as a literal. (If you wish to use arbitrary + expressions, use the empy.setContextName function instead.) + Example: +
+          @?NewName
+          The context name is now @empy.identify()[0] (NewName).
+
+
+
@! INTEGER NEWLINE
+
Set the line number of the current + context to be the given integer value; this is similar to the + #line C preprocessor directive. This is done in such a way + that the next line will have the specified numeric value, not + the current one. Expressions are not allowed here; the number + must be a literal integer. (If you wish to use arbitrary + expressions, use the empy.setContextLine function instead.) + Example: +
+          @!100
+          The context line is now @empy.identify()[1] (100).
+
+
+
@ WHITESPACE
+
A @ followed by one whitespace character + (a space, horizontal tab, vertical tab, carriage return, or + newline) is expanded to nothing; it serves as a way to + explicitly separate two elements which might otherwise be + interpreted as being the same symbol (such as @name@ s to mean + @(name)s -- see below). Also, since a newline qualifies as + whitespace here, the lone @ at the end of a line represents a + line continuation, similar to the backslash in other languages. + Coupled with statement expansion below, spurious newlines can be + eliminated in statement expansions by use of the @{...}@ + construct. Example: +
+          This will appear as one word: salt@ water.
+          This is a line continuation; @
+          this text will appear on the same line.
+
+
+
@\ ESCAPE_CODE
+
An escape code. Escape codes in EmPy are + similar to C-style escape codes, although they all begin with + the prefix character. Valid escape codes include:
+
@\0
+
NUL, null
+
@\a
+
BEL, bell
+
@\b
+
BS, backspace
+
@\d
+
three-digital decimal code DDD
+
@\e
+
ESC, escape
+
@\f
+
FF, form feed
+
@\h
+
DEL, delete
+
@\n
+
LF, linefeed character, newline
+
@\oOOO
+
three-digit octal code OOO
+
@\qQQQQ
+
four-digit quaternary code QQQQ
+
@\r
+
CR, carriage return
+
@\s
+
SP, space
+
@\t
+
HT, horizontal tab
+
@\v
+
VT, vertical tab
+
@\xHH
+
two-digit hexadecimal code HH
+
@\z
+
EOT, end of transmission
+
@^X
+
the control character ^X
+
+

Unlike in C-style escape codes, escape codes taking some number + of digits afterward always take the same number to prevent + ambiguities. Furthermore, unknown escape codes are treated as + parse errors to discourage potential subtle mistakes. Note + that, while @\0 represents the NUL character, to represent an + octal code, one must use @\o..., in contrast to C. Example: +

+          This embeds a newline.@\nThis is on the following line.
+          This beeps!@\a
+          There is a tab here:@\tSee?
+          This is the character with octal code 141: @\o141.
+
+

+
+
@@
+
A literal at sign (@). To embed two adjacent at + signs, use @@@@, and so on. Any literal at sign that you wish + to appear in your text must be written this way, so that it will + not be processed by the system. Note: If a prefix other than + @ has been chosen via the command line option, one expresses + that literal prefix by doubling it, not by appending a @. + Example: +
+          The prefix character is @@.
+          To get the expansion of x you would write @@x.
+
+
+
@), @], @}
+
These expand to literal close parentheses, + close brackets, and close braces, respectively; these are + included for completeness and explicitness only. Example: +
+          This is a close parenthesis: @).
+
+
+
@"...", @"""...""", etc.
+
These string literals expand + to the literals themselves, so @"test" expands to test. + Since they are inherently no-operations, the only reason for + their use is to override their behavior with hooks.
+
@( EXPRESSION )
+
Evaluate an expression, and expand with + the string (via a call to str) representation evaluation of + that expression. Whitespace immediately inside the parentheses + is ignored; @( expression ) is equivalent to @(expression). + If the expression evaluates to None, nothing is expanded in + its place; this allows function calls that depend on side + effects (such as printing) to be called as expressions. (If you + really do want a None to appear in the output, then use the + Python string "None".) Note: If an expression prints + something to sys.stdout as a side effect, then that printing + will be spooled to the output before the expression's return + value is. Example: +
+          2 + 2 is @(2 + 2).
+          4 squared is @(4**2).
+          The value of the variable x is @(x).
+          This will be blank: @(None).
+
+
+
@( TEST ? THEN (! ELSE)_opt ($ EXCEPT)_opt )
+
A special + form of expression evaluation representing conditional and + protected evaluation. Evaluate the "test" expression; if it + evaluates to true (in the Pythonic sense), then evaluate the + "then" section as an expression and expand with the str of + that result. If false, then the "else" section is evaluated and + similarly expanded. The "else" section is optional and, if + omitted, is equivalent to None (that is, no expansion will + take place). Note: For backward compatibility, the "else" + section delimiter, !, may be expressed as a :. This + behavior is supported but deprecated.

If the "except" section is present, then if any of the prior + expressions raises an exception when evaluated, the expansion + will be replaced with the evaluation of the except expression. + (If the "except" expression itself raises, then that exception + will be propagated normally.) The except section is optional + and, if omitted, is equivalent to None (that is, no expansion + will take place). An exception (cough) to this is if one of + these first expressions raises a SyntaxError; in that case the + protected evaluation lets the error through without evaluating + the "except" expression. The intent of this construct is to + except runtime errors, and if there is actually a syntax error + in the "try" code, that is a problem that should probably be + diagnosed rather than hidden. Example: +

+          What is x? x is @(x ? "true" ! "false").
+          Pluralization: How many words? @x word@(x != 1 ? 's').
+          The value of foo is @(foo $ "undefined").
+          Division by zero is @(x/0 $ "illegal").
+
+

+
+
@ SIMPLE_EXPRESSION
+
As a shortcut for the @(...) + notation, the parentheses can be omitted if it is followed by a + "simple expression." A simple expression consists of a name + followed by a series of function applications, array + subscriptions, or attribute resolutions, with no intervening + whitespace. For example: +
    +
  • a name, possibly with qualifying attributes (e.g., + @value, @os.environ).

  • +
  • a straightforward function call (e.g., @min(2, 3), + @time.ctime()), with no space between the function name + and the open parenthesis.

  • +
  • an array subscription (e.g., '@array[index]', + '@os.environ[name]', with no space between the name and + the open bracket.

  • +
  • any combination of the above (e.g., + '@function(args).attr[sub].other[i](foo)').

  • + +
+

In essence, simple expressions are expressions that can be + written ambiguously from text, without intervening space. Note + that trailing dots are not considered part of the expansion + (e.g., @x. is equivalent to @(x)., not @(x.), which + would be illegal anyway). Also, whitespace is allowed within + parentheses or brackets since it is unambiguous, but not between + identifiers and parentheses, brackets, or dots. Explicit + @(...) notation can be used instead of the abbreviation when + concatenation is what one really wants (e.g., @(word)s for + simple pluralization of the contents of the variable word). + As above, if the expression evaluates to the None object, + nothing is expanded. Note that since a curly appearing where + EmPy would expect an open parenthesis or bracket in is + meaningless in Python, it is treated as a parse error (e.g., + @x{1, 2} results in an error). Example: +

+          The value of x is @x.
+          The ith value of a is @a[i].
+          The result of calling f with q is @f(q).
+          The attribute a of x is @x.a.
+          The current time is @time.ctime(time.time()).
+          The current year is @time.localtime(time.time())[0].
+          These are the same: @min(2,3) and @min(2, 3).
+          But these are not the same: @min(2, 3) vs. @min (2, 3).
+          The plural of @name is @(name)s, or @name@ s.
+
+

+
+
@` EXPRESSION `
+
Evaluate a expression, and expand with + the repr (instead of the str which is the default) of the + evaluation of that expression. This expansion is primarily + intended for debugging and is unlikely to be useful in actual + practice. That is, a @`...` is identical to @(repr(...)). + Example: +
+          The repr of the value of x is @`x`.
+          This print the Python repr of a module: @`time`.
+          This actually does print None: @`None`.
+
+
+
@: EXPRESSION : DUMMY :
+
Evaluate an expression and then + expand to a @:, the original expression, a :, the evaluation + of the expression, and then a :. The current contents of the + dummy area are ignored in the new expansion. In this sense it + is self-evaluating; the syntax is available for use in + situations where the same text will be sent through the EmPy + processor multiple times. Example: +
+          This construct allows self-evaluation:
+          @:2 + 2:this will get replaced with 4:
+
+
+
@{ STATEMENTS }
+
Execute a (potentially compound) + statement; statements have no return value, so the expansion is + not replaced with anything. Multiple statements can either be + separated on different lines, or with semicolons; indentation is + significant, just as in normal Python code. Statements, + however, can have side effects, including printing; output to + sys.stdout (explicitly or via a print statement) is + collected by the interpreter and sent to the output (unless this + behavior is suppressed with the -n option). The usual Python + indentation rules must be followed, although if the statement + consists of only one statement, leading and trailing whitespace + is ignored (e.g., @{ print time.time() } is equivalent to + @{print time.time()}). Example: +
+          @{x = 123}
+          @{a = 1; b = 2}
+          @{print time.time()}
+          @# Note that extra newlines will appear above because of the
+          @# newlines trailing the close braces.  To suppress them
+          @# use a @ before the newline:
+          @{
+          for i in range(10):
+              print "i is %d" % i
+          }@
+          @{print "Welcome to EmPy."}@
+
+
+
@% KEY (WHITESPACE VALUE)_opt NEWLINE
+
Declare a + significator. Significators consume the whole line (including + the trailing newline), and consist of a key string containing no + whitespace, and than optional value prefixed by whitespace. The + key may not start with or contain internal whitespace, but the + value may; preceding or following whitespace in the value is + stripped. Significators are totally optional, and are intended + to be used for easy external (that is, outside of EmPy) + identification when used in large scale environments with many + EmPy files to be processed. The purpose of significators is to + provide identification information about each file in a special, + easy-to-parse form so that external programs can process the + significators and build databases, independently of EmPy. + Inside of EmPy, when a significator is encountered, its key, + value pair is translated into a simple assignment of the form + __KEY__ = VALUE , where "__KEY__" is the key string with two + underscores on either side and "VALUE" is a Python expression. + Example: +
+          @%title     "Gravitation"
+          @%author    "Misner", "Thorne", "Wheeler"
+          @%publisher "W.H. Freeman and Company"
+          @%pages     1279
+          @%keywords  'physics', 'gravity', 'Einstein', 'relativity'
+          @%copyright 1970, 1971
+
+
+
**'@< CONTENTS >'**
+
Invoke a custom markup. The custom markup + is a special markup reserved for use by the user; it has no + prescribed meaning on its own. If contents is a string + representing what appears in between the angle brackets, then + expanding this markup is equivalent to + empy.invokeCallback(contents). See the "Custom markup" + section for more information.
+
+

Control

+

EmPy version 3 and above includes the ability to direct + conditional and repeated expansion of blocks of EmPy code with + control markups (the obsolescent "substitution" markups are + unavailable as of version 3.0). Control markups have analogs to + control flow structures in Python such as if/elif/else, for, and + while. Control markups are set off with the @[...] notation.

+

Control markups are designed to be used in precisely the same way + that their internal Python analogues are used, except that the + control markups are intended to be used where there is much more + markup than control structure.

+

Some control markups are considered "primary," (e.g., if, + for, while) as they begin a control markup. Others are + considered "secondary," since they can only appear inside control + flow markups delineated by primary markups (e.g., elif, + else, continue, break).

+

Since EmPy, unlike Python, cannot use indentation to determine + where control structures begin and end, all primary control + markups must be followed by a corresponding terminating control + markup: +

+        @[PRIMARY ...]...@[end PRIMARY]
+
+

+

(where PRIMARY represents one of the primary keywords). The end + markup is mandatory, as is the space between the end and the + starting keyword. For instance: +

+        @# If `person' is alive, show their age.
+        @person.name is @
+        @[if person.isAlive]@person.age@[else]dead@[end if].
+
+

+

All primary markups must be terminated in this way, and the + keyword appearing in the appropriate end markup must match the + primary markup it corresponds to; if either of these conditions + are not satisfied, the result is a parse error. Everything + between the starting control flow marker (@[PRIMARY ...]) and + the ending marker (@[end PRIMARY]) -- including other markups, + even control markups -- is considered part of the markup. Control + markups can be nested: +

+        @# Print all non-false elements on separate lines.
+        @[for elem in elements]@[if elem]@elem@\n@[end if]@[end for]
+
+

+

Three major types of primary control markups are available: + conditional (e.g., if, try), looping (e.g., for, + while), and definitional (e.g., def, discussed below). + Conditional control markups conditionally expand their contents, + whereas looping control markups repeatedly expand their contents. + The third type, definitional markups, will define new objects in + the globals relating to their contents. Conditional and looping + markups also differ in one substantial respect: Looping constructs + support '@[continue]' and '@[break]' markups which, like their + Python equivalents, continue with the next iteration or break out + of the innermost looping construct, respectively ('@[continue]' + and '@[break]' markups have no meaning inside conditional markups + and are an error). Also like their Python equivalents, + '@[continue]' and '@[break]' may appear inside nested markups, so + long as they ultimately are contained by at least one looping + control markup: +

+        @# Walk a long a linked list, printing each element.
+        @[while 1]@
+        @node
+        @{node = node.next}@
+        @[if not node]@[break]@[end if]@
+        @[end while]
+
+

+

The provided markups are designed to mimic the internal Python + control structures as closely as possible. The supported control + markups are (the phrases in all uppercase are intended to signify + user-selectable patterns): +

+        @[if CONDITION1]...@[elif CONDITION2]...@[else]...@[end if]
+        @[try]...@[except ...]...@[except ...]...@[end try]
+        @[try]...@[finally]...@[end try]
+        @[for VARIABLE in SEQUENCE]...@[else]...@[end for]
+        @[while CONDITION]...@[else]...@[end while]
+        @[def SIGNATURE]...@[end def]
+
+

+

All recognizable forms behave like their Python equivalents; if + can contain multiple elif secondary markups within it; the + else markups are optional (but must appear at the end), the + try form with the except clause can contain multiple ones + which are handled in sequence, the try form can either contain + one or more except clauses or one finally clause (but not + both), and the for and while structures can contain continue + or break clauses internally (even if contained within other + markups).

+

The third type of primary control markup is "definitional," in + that they create objects in the globals for later use (e.g., + def). This allows the definition of a callable object which, + when called, will expand the contained markup (which can in turn, + of course, contain further markups). The argument to the markup + can be any legal Python function signature: +

+        @[def f(x, y, z=2, *args, **keywords)]...@[end def]
+
+

+

would define a function in the globals named f that takes the + given arguments. A macro markup of the form @[def + SIGNATURE]CODE@[end def] is equivalent to the Python code: +

+        def SIGNATURE:
+            r"""CODE""" # so it is a doc string
+            empy.expand(r"""CODE""", locals())
+
+

+

That is, it creates a Python function with the same name and + function arguments, whose docstring is the contents of the EmPy + markup that will be expanded when called. And, when called, it + will expand those contents, with the locals passed in.

+

Unicode support

+

EmPy version 3.1 and above includes intrinsic Unicode support. + EmPy's Unicode support defers to Python's internal Unicode + support, available in Python 2.0 and up, in order to allow + seamless and transparent translation of different encodings to the + native Python Unicode format.

+

Knowledge of Python's Unicode support is expected, although not + completely required, to gain full benefit of EmPy's Unicode + features. To enable Unicode support, start EmPy with the + -u/--unicode option. EmPy will then transparently encode from the + input stream, process markups internally with native Unicode, and + then decode transparently to the output stream.

+

By default, Python sets sys.stdin and sys.stdout with a + default encoding which is accessible via + 'sys.getdefaultencoding()'; encodings are represented by string + names. These streams have encodings set by the system and + cannot be changed.

+

However, encodings for newly created files (files to be read when + specified on the command line, and/or files to be written when + used with the -o and -a arguments) can be specified for EmPy via + command line options. The --unicode-encoding option + simultaneously indicates the encoding to be used for both input + and output, whereas the --unicode-input-encoding and + --unicode-output-encoding options can each be used to specify + different encodings for both input and output. (If an encoding is + not explicitly indicated, it resorts to the system default in + sys.getdefaultencoding(), which is locale dependent.)

+

Python's Unicode implementation has the concept of error handlers, + registered with the codecs module, which can be specified to + determine what action should take place if input cannot be decoded + into Unicode, or Unicode cannot be encoded into output. EmPy uses + these same "errors," as they are called, and can be specified via + command line options. The three most common error handlers are: + ignore, where invalid sequences are simply ignored; replace, + where invalid sequences are replaced with an encoding-specific + indicator, usually a question mark; and strict, where invalid + sequences raise an error. The --unicode-errors command line + option specifies the same error handler to be used for both input + and output, and the --unicode-input-errors and + --unicode-output-errors options can specify different error + handlers for input and output. If an error handler is not + explicitly specified, the strict handler (which will raise + errors) is used.

+

Remember, to specify input encodings or errors that will take + effect, one cannot take input from sys.stdin and must explicitly + specify an EmPy file to process on the command line. Similarly, + for output encodings or errors, sys.stdout cannot be used and an + explicit output file must be specified with the -o or -a options. + It is perfectly valid to enable the Unicode subsystem (-u option) + while using sys.stdin and sys.stdout, but the encodings and + errors of these preexisting streams cannot be changed.

+

Combined with the --no-prefix option, which disables all markup + processing, EmPy can act merely as an encoding translator, relying + on Python's Unicode facilities: +

+        em.py --no-prefix \
+            --unicode-input-encoding=utf-8 \
+            --unicode-output-encoding=latin-1 \
+            -o filename.Latin-1 filename.UTF-8
+
+

+

Significators

+

Significators, introduced in EmPy version 1.2, are intended to + represent special assignment in a form that is easy to externally + parse. For instance, if one has a system that contains many EmPy + files, each of which has its own title, one could use a title + significator in each file and use a simple regular expression to + find this significator in each file and organize a database of the + EmPy files to be built. This is an easier proposition than, for + instance, attempting to grep for a normal Python assignment + (inside a @{...} expansion) of the desired variable.

+

Significators look like the following: +

+        @%KEY VALUE
+
+

+

including the trailing newline, where "key" is a name and "value" + is a Python expression, and are separated by any whitespace. This + is equivalent to the following Python code: +

+        __KEY__ = VALUE
+
+

+

That is to say, a significator key translates to a Python variable + consisting of that key surrounded by double underscores on either + side. The value may contain spaces, but the key may not. So: +

+        @%title "All Roads Lead to Rome"
+
+

+

translates to the Python code: +

+        __title__ = "All Roads Lead to Rome"
+
+

+

but obviously in a way that easier to detect externally than if + this Python code were to appear somewhere in an expansion. Since + significator keys are surrounded by double underscores, + significator keys can be any sequence of alphanumeric and + underscore characters; choosing 123 is perfectly valid for a + significator (although straight), since it maps to the name + __123__ which is a legal Python identifier.

+

Note the value can be any Python expression. The value can be + omitted; if missing, it is treated as None.

+

Significators are completely optional; it is completely legal for + a EmPy file or files to be processed without containing any + significators. Significators can appear anywhere within a file + outside of other markups, but typically they are placed near the + top of the file to make them easy to spot and edit by humans.

+

A regular expression string designed to match significators (with + the default prefix) is available as empy.SIGNIFICATOR_RE_STRING, + and also is a toplevel definition in the em module itself.

+

Diversions

+

EmPy supports an extended form of diversions, which are a + mechanism for deferring and recalling output on demand, similar to + the functionality included in m4. Multiple "streams" of output + can be diverted (deferred) and undiverted (recalled) in this + manner. A diversion is identified with a name, which is any + immutable object such an integer or string. When recalled, + diverted code is not resent through the EmPy interpreter + (although a filter could be set up to do this).

+

By default, no diversions take place. When no diversion is in + effect, processing output goes directly to the specified output + file. This state can be explicitly requested at any time by + calling the empy.stopDiverting function. It is always legal to + call this function.

+

When diverted, however, output goes to a deferred location which + can then be recalled later. Output is diverted with the + empy.startDiversion function, which takes an argument that is + the name of the diversion. If there is no diversion by that name, + a new diversion is created and output will be sent to that + diversion; if the diversion already exists, output will be + appended to that preexisting diversion.

+

Output send to diversions can be recalled in two ways. The first + is through the empy.playDiversion function, which takes the + name of the diversion as an argument. This recalls the named + diversion, sends it to the output, and then erases that + diversion. A variant of this behavior is the + empy.replayDiversion, which recalls the named diversion but does + not eliminate it afterwards; empy.replayDiversion can be + repeatedly called with the same diversion name, and will replay + that diversion repeatedly. empy.createDiversion create a + diversion without actually diverting to it, for cases where you + want to make sure a diversion exists but do not yet want to send + anything to it.

+

The diversion object itself can be retrieved with + empy.retrieveDiversion. Diversions act as writable + file-objects, supporting the usual write, writelines, flush, + and close methods. The data that has been diverted to them can + be retrieved in one of two ways; either through the asString + method, which returns the entire contents of the diversion as a + single strong, or through the asFile method, which returns the + contents of the diversion as a readable (not writable) file-like + object.

+

Diversions can also be explicitly deleted without recalling them + with the empy.purgeDiversion function, which takes the desired + diversion name as an argument.

+

Additionally there are three functions which will apply the above + operations to all existing diversions: empy.playAllDiversions, + empy.replayAllDiversions, and empy.purgeAllDiversions. All + three will do the equivalent of a empy.stopDiverting call before + they do their thing.

+

The name of the current diversion can be requested with the + empy.getCurrentDiversion function; also, the names of all + existing diversions (in sorted order) can be retrieved with + empy.getAllDiversions.

+

When all processing is finished, the equivalent of a call to + empy.playAllDiversions is done.

+

Filters

+

EmPy also supports dynamic filters, introduced in version 1.3. + Filters are put in place right "before" the final output file, and + so are only invoked after all other processing has taken place + (including interpreting and diverting). Filters take input, remap + it, and then send it to the output.

+

The current filter can be retrieved with the empy.getFilter + function. The filter can be cleared (reset to no filter) with + empy.resetFilter and a special "null filter" which does not send + any output at all can be installed with empy.nullFilter. A + custom filter can be set with the empy.setFilter function; for + convenience, specialized shortcuts for filters preexist and can be + used in lieu of actual empy.Filter instances for the + empy.setFilter or empy.attachFilter argument:

+ +
    +
  • None is a special filter meaning "no filter"; when installed, + no filtering whatsoever will take place. empy.setFilter(None) + is equivalent to empy.resetFilter().

  • +
  • 0 (or any other numeric constant equal to zero) is another + special filter that represents the null filter; when installed, + no output will ever be sent to the filter's sink.

  • +
  • A filter specified as a function (or lambda) is expected to take + one string argument and return one string argument; this filter + will execute the function on any input and use the return value + as output.

  • +
  • A filter that is a string is a 256-character table is + substituted with the result of a call to string.translate + using that table.

  • +
  • A filter can be an instance of a subclass of empy.Filter. + This is the most general form of filter. (In actuality, it can + be any object that exhibits a Filter interface, which would + include the normal file-like write, flush, and close + methods, as well as next, attach, and detach methods for + filter-specific behavior.)

  • +
  • Finally, the argument to empy.setFilter can be a Python list + consisting of one or more of the above objects. In that case, + those filters are chained together in the order they appear in + the list. An empty list is the equivalent of 'None'; all + filters will be uninstalled.

  • + +
+

Filters are, at their core, simply file-like objects (minimally + supporting write, flush, and close methods that behave in + the usual way) which, after performing whatever processing they + need to do, send their work to the next file-like object or filter + in line, called that filter's "sink." That is to say, filters can + be "chained" together; the action of each filter takes place in + sequence, with the output of one filter being the input of the + next. Additionally, filters support a _flush method (note the + leading underscore) which will always flush the filter's + underlying sink; this method should be not overridden.

+

Filters also support three additional methods, not part of the + traditional file interface: attach, which takes as an argument a + file-like object (perhaps another filter) and sets that as the + filter's "sink" -- that is, the next filter/file-like object in + line. detach (which takes no arguments) is another method which + flushes the filter and removes its sink, leaving it isolated. + Finally, next is an accessor method which returns the filter's + sink -- or None, if the filter does not yet have a sink + attached.

+

To create your own filter, you can create an object which supports + the above described interface, or simply derive from the + empy.Filter class and override its write and possibly flush + methods. You can chain filters together by passing them as + elements in a list to the empy.setFilter function, or you can + chain them together manually with the attach method: +

+        firstFilter.attach(secondFilter)
+        empy.setFilter(firstFilter)
+
+

+

or just let EmPy do the chaining for you: +

+        empy.setFilter([firstFilter, secondFilter])
+
+

+

In either case, EmPy will walk the filter chain and find the end + and then hook that into the appropriate interpreter stream; you + need not do this manually. The function empy.attachFilter can + be used to attach a single filter (or shortcut, as above) to the + end of a currently existing chain. Note that unlike its cousin + empy.setFilter, one cannot pass a sequence of filters (or filter + shortcuts) to empy.attachFilter. (If there is no existing + filter chain installed, empy.attachFilter will behave the same + as empy.setFilter.)

+

Subclasses of empy.Filter are already provided with the above + null, function, and string functionality described above; they are + NullFilter, FunctionFilter, and StringFilter, respectively. + In addition, a filter which supports buffering, BufferedFilter, + is provided. Several variants are included: SizeBufferedFilter, + a filter which buffers into fixed-sized chunks, + LineBufferedFilter, a filter which buffers by lines, and + MaximallyBufferedFilter, a filter which completely buffers its + input.

+

Hooks

+

The EmPy system allows for the registry of hooks with a running + EmPy interpreter. Originally introduced in version 2.0 and much + improved in 3.2, hooks are objects, registered with an + interpreter, whose methods represent specific callbacks. Any + number of hook objects can be registered with an interpreter, and + when a callback is invoked, the associated method on each one of + those hook objects will be called by the interpreter in sequence.

+

Hooks are simply instances, nominally derived from the empy.Hook + class. The empy.Hook class itself defines a series of methods, + with the expected arguments, which would be called by a running + EmPy interpreter. This scenario, much improved from the prior + implementation in 2.0, allows hooks to keep state and have more + direct access to the interpreter they are running in (the + empy.Hook instance contains an interpreter attribute).

+

To use a hook, derive a class from empy.Hook and override the + desired methods (with the same signatures as they appear in the + base class). Create an instance of that subclass, and then + register it with a running interpreter with the empy.addHook + function. (This same hook instance can be removed with the + empy.removeHook function.)

+

More than one hook instance can be registered with an interpreter; + in such a case, the appropriate methods are invoked on each + instance in the order in which they were registered. To adjust + this behavior, an optional prepend argument to the + empy.addHook function can be used dictate that the new hook + should placed at the beginning of the sequence of hooks, rather + than at the end (which is the default).

+

All hooks can be enabled and disabled entirely for a given + interpreter; this is done with the empy.enableHooks and + empy.disableHooks functions. By default hooks are enabled, but + obviously if no hooks have been registered no hook callbacks will + be made. Whether hooks are enabled or disabled can be determined + by calling empy.areHooksEnabled. To get a (copy of) the list of + registered hooks, call empy.getHooks. Finally, to invoke a hook + manually, use empy.invokeHook.

+

For a list of supported hook callbacks, see the empy.Hook class + definition.

+

As a practical example, this sample Python code would print a + pound sign followed by the name of every file that is included + with 'empy.include': +

+        class IncludeHook(empy.Hook):
+            def beforeInclude(self, name, file, locals):
+                print "# %s" % name
+
+        empy.addHook(IncludeHook())
+
+

+

Custom markup

+

Since version 3.2.1, the markup '@<...>' is reserved for + user-defined use. Unlike the other markups, this markup has no + specified meaning on its own, and can be provided a meaning by the + user. This meaning is provided with the use of a "custom + callback," or just "callback," which can be set, queried, or reset + using the pseudomodule function.

+

The custom callback is a callable object which, when invoked, is + passed a single argument: a string representing the contents of + what was found inside the custom markup '@<...>'.

+

To register a callback, call empy.registerCallback. To remove + one, call empy.deregisterCallback. To retrieve the callback (if + any) registered with the interpreter, use empy.getCallback. + Finally, to invoke the callback just as if the custom markup were + encountered, call empy.invokeCallback. For instance, '@' would be equivalent to the call @empy.invokeCallback("This + text").

+

By default, to invoke a callback (either explicitly with + empy.invokeCallback or by processing a '@<...>' custom markup) + when no callback has been registered is an error. This behavior + can be changed with the CALLBACK_OPT option, or the + --no-callback-error command line option.

+

Pseudomodule

+

The empy pseudomodule is available only in an operating EmPy + system. (The name of the module, by default empy, can be + changed with the -m option or the EMPY_PSEUDO environment + variable). It is called a pseudomodule because it is not actually + a module, but rather exports a module-like interface. In fact, + the pseudmodule is actually the same internal object as the + interpreter itself.

+

The pseudomodule contains the following functions and objects (and + their signatures, with a suffixed opt indicating an optional + argument):

+

First, basic identification:

+
+
VERSION
+
A constant variable which contains a + string representation of the EmPy version.
+
SIGNIFICATOR_RE_STRING
+
A constant variable representing a + regular expression string (using the default prefix) that can be + used to find significators in EmPy code.
+
SIGNIFICATOR_RE_SUFFIX
+
The portion of the significator + regular expression string excluding the prefix, so that those + using non-standard prefix can build their own custom regular + expression string with myPrefix + empy.SIGNIFICATOR_RE_SUFFIX.
+
interpreter
+
The instance of the interpreter that is + currently being used to perform execution. Note: This is now + obsolete; the pseudomodule is itself the interpreter. Instead + of using empy.interpreter, simply use empy.
+
argv
+
A list consisting of the name of the primary EmPy + script and its command line arguments, in analogue to the + sys.argv list.
+
args
+
A list of the command line arguments following the + primary EmPy script; this is equivalent to empy.argv[1:].
+
identify() -> string, integer
+
Retrieve identification + information about the current parsing context. Returns a + 2-tuple consisting of a filename and a line number; if the file + is something other than from a physical file (e.g., an + explicit expansion with empy.expand, a file-like object within + Python, or via the -E or -F command line options), a string + representation is presented surrounded by angle brackets. Note + that the context only applies to the EmPy context, not the + Python context.
+
atExit(callable)
+
Register a callable object (such as a + function) taking no arguments which will be called at the end of + a normal shutdown. Callable objects registered in this way are + called in the reverse order in which they are added, so the + first callable registered with empy.atExit is the last one to + be called. Note that although the functionality is related to + hooks, empy.atExit does no work via the hook mechanism, and + you are guaranteed that the interpreter and stdout will be in a + consistent state when the callable is invoked.
+
+

Context manipulation:

+
+
pushContext(name_opt, line_opt)
+
Create a new context with + the given name and line and push it on the stack.
+
popContext()
+
Pop the top context and dispose of it.
+
setContextName(name)
+
Manually set the name of the current + context.
+
setContextLine(line)
+
Manually set the line number of the + current context; line must be a numeric value. Note that + afterward the line number will increment by one for each newline + that is encountered, as before.
+
+

Globals manipulation:

+
+
getGlobals()
+
Retrieve the globals dictionary for this + interpreter. Unlike when calling globals() in Python, this + dictionary can be manipulated and you can expect changes you + make to it to be reflected in the interpreter that holds it.
+
setGlobals(globals)
+
Reseat the globals dictionary + associated with this interpreter to the provided mapping type.
+
updateGlobals(globals)
+
Merge the given dictionary into + this interpreter's globals.
+
clearGlobals(globals_opt)
+
Clear out the globals + (restoring, of course, the empy pseudomodule). Optionally, + instead of starting with a refresh dictionary, use the + dictionary provided.
+
saveGlobals(deep=True)
+
Save a copy of the globals onto an + internal history stack from which it can be restored later. The + optional deep argument indicates whether or not the copying + should be a deep copy (default) or a shallow one. Copying is + done with copy.deepcopy or copy.copy, respectively.
+
restoreGlobals(destructive=True)
+
Restore the most + recently saved globals from the history stack to as the current + globals for this instance. The optional destructive argument + indicates whether or not the restore should remove the restored + globals from the history stack (default), or whether it should + be left there for subsequent restores.
+
+

Types:

+
+
Interpreter
+
The actual interpreter class.
+
+

The following functions allow direct execution; optional locals + arguments, if specified, are treated as the locals dictionary in + evaluation and execution:

+
+
defined(name, locals_opt)
+
Return true if the given name + is defined either in the (optional) locals or the interpreter + globals; return false otherwise.
+
evaluate(expression, locals_opt)
+
Evaluate the given + expression.
+
serialize(expression, locals_opt)
+
Serialize the + expression, just as the interpreter would: If it is not None, + convert it to a string with the str builtin function, and then + write out the result. If it evaluates to None, do nothing.
+
execute(statements, locals_opt)
+
Execute the given + statement(s).
+
single(source, locals_opt)
+
Interpret the "single" source + code, just as the Python interactive interpreter would.
+
import_(name, locals_opt)
+
Import a module.
+
atomic(name, value, locals_opt)
+
Perform a single, atomic + assignment. In this case name is the string denoating the name + of the (single) variable to be assigned to, and value is a + Python object which the name is to be bound to.
+
assign(name, value, locals_opt)
+
Perform general + assignment. This decays to atomic assignment (above) in the + normal case, but supports "tuple unpacking" in the sense that if + name string contains commas, it is treated as a sequence of + names and memberwise assignment with each member of the value + (still a Python object, but which must be a sequence). This + function will raise a TypeError or ValueError just like + Python would if tuple unpacking is not possible (that is, if the + value is not a sequence or is of an incompatible length, + respectively). This only supports the assignment of Python + identifiers, not arbitrary Python lvalues.
+
significate(key, value_opt, locals_opt)
+
Do a manual + signification. If value is not specified, it is treated as + None.
+
+

The following functions relate to source manipulation:

+
+
include(file_or_filename, locals_opt)
+
Include another + EmPy file, by processing it in place. The argument can either + be a filename (which is then opened with open in text mode) or + a file object, which is used as is. Once the included file is + processed, processing of the current file continues. Includes + can be nested. The call also takes an optional locals + dictionary which will be passed into the evaluation function.
+
expand(string, locals_opt) -> string
+
Explicitly invoke + the EmPy parsing system to process the given string and return + its expansion. This allows multiple levels of expansion, + e.g., @(empy.expand("@(2 + 2)")). The call also takes an + optional locals dictionary which will be passed into the + evaluation function. This is necessary when text is being + expanded inside a function definition and it is desired that the + function arguments (or just plain local variables) are available + to be referenced within the expansion.
+
quote(string) -> string
+
The inverse process of + empy.expand, this will take a string and return a new string + that, when expanded, would expand to the original string. In + practice, this means that appearances of the prefix character + are doubled, except when they appear inside a string literal.
+
escape(string, more_opt) -> string
+
Given a string, quote + the nonprintable characters contained within it with EmPy + escapes. The optional more argument specifies additional + characters that should be escaped.
+
flush()
+
Do an explicit flush on the underlying stream.
+
string(string, name_opt, locals_opt)
+
Explicitly process a + string-like object. This differs from empy.expand in that the + string is directly processed into the EmPy system, rather than + being evaluated in an isolated context and then returned as a + string.
+
+

Changing the behavior of the pseudomodule itself:

+
+
flatten(keys_opt)
+
Perform the equivalent of from empy + import ... in code (which is not directly possible because + empy is a pseudomodule). If keys is omitted, it is taken as + being everything in the empy pseudomodule. Each of the + elements of this pseudomodule is flattened into the globals + namespace; after a call to empy.flatten, they can be referred + to simple as globals, e.g., @divert(3) instead of + @empy.divert(3). If any preexisting variables are bound to + these names, they are silently overridden. Doing this is + tantamount to declaring an from ... import ... which is often + considered bad form in Python.
+
+

Prefix-related functions:

+
+
getPrefix() -> char
+
Return the current prefix.
+
setPrefix(char)
+
Set a new prefix. Immediately after this + call finishes, the prefix will be changed. Changing the prefix + affects only the current interpreter; any other created + interpreters are unaffected. Setting the prefix to None or the + null string means that no further markups will be processed, + equivalent to specifying the --no-prefix command line argument.
+
+

Diversions:

+
+
stopDiverting()
+
Any diversions that are currently taking + place are stopped; thereafter, output will go directly to the + output file as normal. It is never illegal to call this + function.
+
createDiversion(name)
+
Create a diversion, but do not + begin diverting to it. This is the equivalent of starting a + diversion and then immediately stopping diversion; it is used in + cases where you want to make sure that a diversion will exist + for future replaying but may be empty.
+
startDiversion(name)
+
Start diverting to the specified + diversion name. If such a diversion does not already exist, it + is created; if it does, then additional material will be + appended to the preexisting diversions.
+
playDiversion(name)
+
Recall the specified diversion and + then purge it. The provided diversion name must exist.
+
replayDiversion(name)
+
Recall the specified diversion + without purging it. The provided diversion name must exist.
+
purgeDiversion(name)
+
Purge the specified diversion + without recalling it. The provided diversion name must exist.
+
playAllDiversions()
+
Play (and purge) all existing + diversions in the sorted order of their names. This call does + an implicit empy.stopDiverting before executing.
+
replayAllDiversions()
+
Replay (without purging) all + existing diversions in the sorted order of their names. This + call does an implicit empy.stopDiverting before executing.
+
purgeAllDiversions()
+
Purge all existing diversions + without recalling them. This call does an implicit + empy.stopDiverting before executing.
+
getCurrentDiversion() -> diversion
+
Return the name of the + current diversion.
+
getAllDiversions() -> sequence
+
Return a sorted list of + all existing diversions.
+
+

Filters:

+
+
getFilter() -> filter
+
Retrieve the current filter. + None indicates no filter is installed.
+
resetFilter()
+
Reset the filter so that no filtering is + done.
+
nullFilter()
+
Install a special null filter, one which + consumes all text and never sends any text to the output.
+
setFilter(shortcut)
+
Install a new filter. A filter is + None or an empty sequence representing no filter, or 0 for a + null filter, a function for a function filter, a string for a + string filter, or an instance of empy.Filter (or a workalike + object). If filter is a list of the above things, they will be + chained together manually; if it is only one, it will be + presumed to be solitary or to have already been manually chained + together. See the "Filters" section for more information.
+
attachFilter(shortcut)
+
Attach a single filter (sequences + are not allowed here) to the end of a currently existing filter + chain, or if there is no current chain, install it as + empy.setFilter would. As with empy.setFilter, the shortcut + versions of filters are also allowed here.
+
+

Hooks:

+
+
areHooksEnabled()
+
Return whether or not hooks are + presently enabled.
+
enableHooks()
+
Enable invocation of hooks. By default + hooks are enabled.
+
disableHooks()
+
Disable invocation of hooks. Hooks can + still be added, removed, and queried, but invocation of hooks + will not occur (even explicit invocation with + empy.invokeHook).
+
getHooks()
+
Get a (copy of the) list of the hooks + currently registered.
+
clearHooks()
+
Clear all the hooks registered with this + interpreter.
+
addHook(hook, prepend_opt)
+
Add this hook to the hooks + associated with this interpreter. By default, the hook is + appended to the end of the existing hooks, if any; if the + optional insert argument is present and true, it will be + prepended to the list instead.
+
removeHook(hook)
+
Remove this hook from the hooks + associated with this interpreter.
+
invokeHook(_name, ...)
+
Manually invoke a hook method. + The remaining arguments are treated as keyword arguments and the + resulting dictionary is passed in as the second argument to the + hooks.
+
+

Custom markup callback:

+
+
getCallback() -> callback
+
Retrieve the current callback + associated with this interpreter, or None if it does not yet + have one.
+
registerCallback(callback)
+
Register a callback to be + called whenever a custom markup ('@<...>') is encountered. When + encountered, invokeCallback is called.
+
deregisterCallback()
+
Clear any callback previously + registered with the interpreter for being called when a custom + markup is encountered.
+
invokeCallback(contents)
+
Invoke a custom callback. This + function is called whenever a custom markup ('@<...>') is + encountered. It in turn calls the registered callback, with a + single argument, contents, which is a string representing of + the contents of the custom markup.
+
+

Invocation

+

Basic invocation involves running the interpreter on an EmPy file + and some optional arguments. If no file are specified, or the + file is named -, EmPy takes its input from stdin. One can + suppress option evaluation (to, say, specify a file that begins + with a dash) by using the canonical -- option.

+
+
-h/--help
+
Print usage and exit.
+
-H/--extended-help
+
Print extended usage and exit. + Extended usage includes a rundown of all the legal expansions, + escape sequences, pseudomodule contents, used hooks, and + supported environment variables.
+
-v/--verbose
+
The EmPy system will print all manner of + details about what it is doing and what it is processing to + stderr.
+
-V/--version
+
Print version and exit.
+
-a/--append (filename)
+
Open the specified file for + append instead of using stdout.
+
-b/--buffered-output
+
Fully buffer processing output, + including the file open itself. This is helpful when, should an + error occur, you wish that no output file be generated at all + (for instance, when using EmPy in conjunction with make). When + specified, either the -o or -a options must be specified, and + the -b option must precede them. This can also be specified + through the existence of the EMPY_BUFFERED_OUTPUT environment + variable.
+
-f/--flatten
+
Before processing, move the contents of + the empy pseudomodule into the globals, just as if + empy.flatten() were executed immediately after starting the + interpreter. That is, e.g., empy.include can be referred to + simply as include when this flag is specified on the command + line. This can also be specified through the existence of the + EMPY_FLATTEN environment variable.
+
-i/--interactive
+
After the main EmPy file has been + processed, the state of the interpreter is left intact and + further processing is done from stdin. This is analogous to the + Python interpreter's -i option, which allows interactive + inspection of the state of the system after a main module is + executed. This behaves as expected when the main file is stdin + itself. This can also be specified through the existence of the + EMPY_INTERACTIVE environment variable.
+
-k/--suppress-errors
+
Normally when an error is + encountered, information about its location is printed and the + EmPy interpreter exits. With this option, when an error is + encountered (except for keyboard interrupts), processing stops + and the interpreter enters interactive mode, so the state of + affairs can be assessed. This is also helpful, for instance, + when experimenting with EmPy in an interactive manner. -k + implies -i.
+
-n/--no-override-stdout
+
Do not override sys.stdout + with a proxy object which the EmPy system interacts with. If + suppressed, this means that side effect printing will not be + captured and routed through the EmPy system. However, if this + option is specified, EmPy can support multithreading.
+
-o/--output (filename)
+
Open the specified file for + output instead of using stdout. If a file with that name + already exists it is overwritten.
+
-p/--prefix (prefix)
+
Change the prefix used to detect + expansions. The argument is the one-character string that will + be used as the prefix. Note that whatever it is changed to, the + way to represent the prefix literally is to double it, so if $ + is the prefix, a literal dollar sign is represented with $$. + Note that if the prefix is changed to one of the secondary + characters (those that immediately follow the prefix to indicate + the type of action EmPy should take), it will not be possible to + represent literal prefix characters by doubling them (e.g., if + the prefix were inadvisedly changed to # then ## would + already have to represent a comment, so ## could not represent + a literal #). This can also be specified through the + EMPY_PREFIX environment variable.
+
-r/--raw-errors
+
Normally, EmPy catches Python + exceptions and prints them alongside an error notation + indicating the EmPy context in which it occurred. This option + causes EmPy to display the full Python traceback; this is + sometimes helpful for debugging. This can also be specified + through the existence of the EMPY_RAW_ERRORS environment + variable.
+
-u/--unicode
+
Enable the Unicode subsystem. This option + only need be present if you wish to enable the Unicode subsystem + with the defaults; any other Unicode-related option (starting + with --unicode...) will also enable the Unicode subsystem.
+
-D/--define (assignment)
+
Execute a Python assignment of + the form variable = expression. If only a variable name is + provided (i.e., the statement does not contain an = sign), + then it is taken as being assigned to None. The -D option is + simply a specialized -E option that special cases the lack of an + assignment operator. Multiple -D options can be specified.
+
-E/--execute (statement)
+
Execute the Python (not EmPy) + statement before processing any files. Multiple -E options can + be specified.
+
-F/--execute-file (filename)
+
Execute the Python (not + EmPy) file before processing any files. This is equivalent to + -E execfile("filename") but provides a more readable context. + Multiple -F options can be specified.
+
-I/--import (module)
+
Imports the specified module name + before processing any files. Multiple modules can be specified + by separating them by commas, or by specifying multiple -I + options.
+
-P/--preprocess (filename)
+
Process the EmPy file before + processing the primary EmPy file on the command line.
+
--binary
+
Treat the file as a binary file, and read in + chunks rather than line by line. In this mode, the "line" + indicator represents the number of bytes read, not the number of + lines processed.
+
--no-prefix
+
Disable the prefixing system entirely; when + specified, EmPy will not expand any markups. This allows EmPy + to merely act as a Unicode encoding translator..
+
--pause-at-end
+
If present, then raw_input will be + called at the end of processing. Useful in systems where the + output window would otherwise be closed by the operating + system/window manager immediately after EmPy exited.
+
--relative-path
+
When present, the path the EmPy script + being invoked is contained in will be prepended to sys.path. + This is analogous to Python's internal handling of sys.path + and scripts. If input is from stdin (- for a filename or no + filename is specified), then nothing is added to the path.
+
--no-callback-error
+
Do not consider it an error if the + custom markup is invoked '@<...>' and there is no callback + function registered for it.
+
--chunk-size (chunk)
+
Use the specific binary chunk size + rather than the default; implies --binary.
+
--unicode-encoding (encoding)
+
Specify the Unicode + encoding to be used for both input and output.
+
--unicode-input-encoding (encoding)
+
Specify the Unicode + encoding to be used for input.
+
--unicode-output-encoding (encoding)
+
Specify the Unicode + encoding to be used for output.
+
'--unicode-input-errors (errors)
+
Specify the Unicode error + handling to be used for input.
+
'--unicode-errors (errors)
+
Specify the Unicode error + handling to be used for both input and output.
+
'--unicode-output-errors (errors)
+
Specify the Unicode error + handling to be used for output.
+
+

Environment variables

+

EmPy also supports a few environment variables to predefine + certain behaviors. The settings chosen by environment variables + can be overridden via command line arguments. The following + environment variables have meaning to EmPy:

+
+
EMPY_OPTIONS
+
If present, the contents of this environment + variable will be treated as options, just as if they were + entered on the command line, before the actual command line + arguments are processed. Note that these arguments are not + processed by the shell, so quoting, filename globbing, and the + like, will not work.
+
EMPY_PREFIX
+
If present, the value of this environment + variable represents the prefix that will be used; this is + equivalent to the -p command line option.
+
EMPY_PSEUDO
+
If present, the value of this environment + variable represents the name of the pseudomodule that will be + incorporated into every running EmPy system; this is equivalent + to the -m command line option.
+
EMPY_FLATTEN
+
If defined, this is equivalent to including + -f on the command line.
+
EMPY_RAW_ERRORS
+
If defined, this is equivalent to + including -r on the command line.
+
EMPY_INTERACTIVE
+
If defined, this is equivalent to + including -i on the command line.
+
EMPY_BUFFERED_OUTPUT
+
If defined, this is equivalent to + including -b on the command line.
+
EMPY_UNICODE
+
If defined, this is equivalent to including + -u on the command line.
+
EMPY_UNICODE_INPUT_ENCODING
+
If present, the value of this + environment variable indicates the name of the Unicode input + encoding to be used. This is equivalent to the + --unicode-input-encoding command line option.
+
EMPY_UNICODE_OUTPUT_ENCODING
+
If present, the value of + this environment variable indicates the name of the Unicode + output encoding to be used. This is equivalent to the + --unicode-output-encoding command line option.
+
EMPY_UNICODE_INPUT_ERRORS
+
If present, the value of this + environment variable indicates the name of the error handler to + be used for input. This is equivalent to the + --unicode-input-errors command line option.
+
EMPY_UNICODE_OUTPUT_ERRORS
+
If present, the value of this + environment variable indicates the name of the error handler to + be used for output. This is equivalent to the + --unicode-output-errors command line option.
+
+

Examples and testing EmPy

+

See the sample EmPy file sample.em which is included with the + distribution. Run EmPy on it by typing something like: +

+         ./em.py sample.em
+
+

+

and compare the results and the sample source file side by side. + The sample content is intended to be self-documenting, and even an + introduction to the basic features of EmPy while simultaneously + exercising them.

+

The file sample.bench is the benchmark output of the sample. + Running the EmPy interpreter on the provided sample.em file + should produce precisely the same results. You can run the + provided test script to see if your EmPy environment is behaving + as expected (presuming a Unix-like operating system): +

+        ./test.sh
+
+

+

By default this will test with the first Python interpreter + available in the path; if you want to test with another + interpreter, you can provide it as the first argument on the + command line, e.g.: +

+        ./test.sh python2.1
+        ./test.sh /usr/bin/python1.5
+        ./test.sh jython
+
+

+

A more comprehensive test suite and set of real-world examples is + planned for a future version.

+

Embedding EmPy

+

For atomic applications, the expand function is provided (the + extra keyword arguments passed in are treated as locals): +

+        import em
+        print em.expand("@x + @y is @(x + y).", x=2, y=3)
+
+

+

One can specify a globals dictionary and all the other interpreter + options (below) as well. One can specify a globals dictionary + that will be used if one wants persistence: +

+        import em
+        g = {}
+        em.expand("@{x = 10}", g)
+        print em.expand("x is @x.", g)
+
+

+

The standalone expand function, however, creates and destroys an + Interpreter instance each time it is called. For repeated + expansions, this can be expensive. Instead, you will probably + want to use the full-fledged features of embedding. An EmPy + interpreter can be created with as code as simple as: +

+        import em
+        interpreter = em.Interpreter()
+        # The following prints the results to stdout:
+        interpreter.string("@{x = 123}@x\n")
+        # This expands to the same thing, but puts the results as a
+        # string in the variable result:
+        result = interpreter.expand("@{x = 123}@x\n")
+        # This just prints the value of x directly:
+        print interpreter.globals['x']
+        # Process an actual file (and output to stdout):
+        interpreter.file(open('/path/to/some/file'))
+        interpreter.shutdown() # this is important; see below
+
+

+

One can capture the output of a run in something other than stdout + by specifying the output parameter: +

+        import em, StringIO
+        output = StringIO.StringIO()
+        interpreter = em.Interpreter(output=output)
+        # Do something.
+        interpreter.file(open('/path/to/some/file'))
+        interpreter.shutdown() # again, this is important; see below
+        print output.getvalue() # this is the result from the session
+
+

+

When you are finished with your interpreter, it is important to + call its shutdown method: +

+        interpreter.shutdown()
+
+

+

This will ensure that the interpreter cleans up all its overhead, + entries in the sys.stdout proxy, and so forth. It is usually + advisable that this be used in a try...finally clause: +

+        interpreter = em.Interpreter(...)
+        try:
+            ...
+        finally:
+            interpreter.shutdown()
+
+

+

The em.Interpreter constructor takes the following arguments; + all are optional. Since options may be added in the future, it is + highly recommended that the constructor be invoked via keyword + arguments, rather than assuming their order. The arguments are:

+
+
output
+
The output file which the interpreter will be sending + all its processed data to. This need only be a file-like object; + it need not be an actual file. If omitted, sys.__stdout__ is + used.
+
argv
+
An argument list analogous to sys.argv, consisting of + the script name and zero or more arguments. These are available + to executing interpreters via empy.argv and empy.args. If + omitted, a non-descript script name is used with no arguments.
+
prefix
+
The prefix (a single-character string). Defaults to + @. It is an error for this to be anything other than one + character.
+
pseudo
+
The name (string) of the pseudmodule. Defaults to + empy.
+
options
+
A dictionary of options that can override the default + behavior of the interpreter. The names of the options are + constant names ending in _OPT and their defaults are given in + Interpreter.DEFAULT_OPTIONS.
+
globals
+
By default, interpreters begin with a pristine + dictionary of globals (except, of course, for the empy + pseudomodule). Specifying this argument will allow the globals + to start with more.
+
hooks
+
A sequence of hooks (or None for none) to register + with the interpreter at startup. Hooks can, of course, be added + after the fact, but this allows the hooks to intercept the + atStartup event (otherwise, the startup event would already + have occurred by the time new hooks could be registered)..
+
+

Many things can be done with EmPy interpreters; for the full + developer documentation, see the generated documentation for the + em module.

+

Interpreter options

+

The following options (passed in as part of the options dictionary + to the Interpreter constructor) have the following meanings. The + defaults are shown below and are also indicated in an + Interpreter.DEFAULT_OPTIONS dictionary.

+
+
BANGPATH_OPT
+
Should a bangpath (#!) as the first line + of an EmPy file be treated as if it were an EmPy comment? Note + that #! sequences starting lines or appearing anywhere else in + the file are untouched regardless of the value of this option. + Default: true.
+
BUFFERED_OPT
+
Should an abort method be called upon + failure? This relates to the fully-buffered option, where all + output can be buffered including the file open; this option only + relates to the interpreter's behavior after that proxy file + object has been created. Default: false.
+
RAW_OPT
+
Should errors be displayed as raw Python errors + (that is, the exception is allowed to propagate through to the + toplevel so that the user gets a standard Python traceback)? + Default: false.
+
EXIT_OPT
+
Upon an error, should execution continue + (although the interpreter stacks will be purged)? Note that + even in the event this is set, the interpreter will halt upon + receiving a KeyboardInterrupt. Default: true.
+
FLATTEN_OPT
+
Upon initial startup, should the empy + pseudomodule namespace be flattened, i.e., should + empy.flatten be called? Note this option only has an effect + when the interpreter is first created; thereafter it is + ignored. Default: false.
+
OVERRIDE_OPT
+
Should the sys.stdout object be overridden + with a proxy object? If not, side effect output cannot be + captured by the EmPy system, but EmPy will support + multithreading. Default: true.
+
CALLBACK_OPT
+
If a callback is invoked when none has yet + been registered, should an error be raised or should the + situation be ignored? Default: true.
+
+

Data flow

+

input -> interpreter -> diversions -> filters -> output

+

Here, in summary, is how data flows through a working EmPy system:

+ +
    +
  1. Input comes from a source, such an .em file on the command + line, or via an empy.include statement.

  2. +
  3. The interpreter processes this material as it comes in, + expanding EmPy expansions as it goes.

  4. +
  5. After interpretation, data is then sent through the diversion + layer, which may allow it directly through (if no diversion is + in progress) or defer it temporarily. Diversions that are + recalled initiate from this point.

  6. +
  7. Any filters in place are then used to filter the data and + produce filtered data as output.

  8. +
  9. Finally, any material surviving this far is sent to the output + stream. That stream is stdout by default, but can be changed + with the -o or -a options, or may be fully buffered with the -b + option (that is, the output file would not even be opened until + the entire system is finished).

  10. + +
+

Author's notes

+

I originally conceived EmPy as a replacement for my Web + templating system which + uses m4 (a general + macroprocessing system for Unix).

+

Most of my Web sites include a variety of m4 files, some of which + are dynamically generated from databases, which are then scanned + by a cataloging tool to organize them hierarchically (so that, + say, a particular m4 file can understand where it is in the + hierarchy, or what the titles of files related to it are without + duplicating information); the results of the catalog are then + written in database form as an m4 file (which every other m4 file + implicitly includes), and then GNU make converts each m4 to an + HTML file by processing it.

+

As the Web sites got more complicated, the use of m4 (which I had + originally enjoyed for the challenge and abstractness) really + started to become an impediment to serious work; while I am very + knowledgeable about m4 -- having used it for for so many years -- + getting even simple things done with it is awkward and difficult. + Worse yet, as I started to use Python more and more over the + years, the cataloging programs which scanned the m4 and built m4 + databases were migrated to Python and made almost trivial, but + writing out huge awkward tables of m4 definitions simply to make + them accessible in other m4 scripts started to become almost + farcical -- especially when coupled with the difficulty in getting + simple things done in m4.

+

It occurred to me what I really wanted was an all-Python solution. + But replacing what used to be the m4 files with standalone Python + programs would result in somewhat awkward programs normally + consisting mostly of unprocessed text punctuated by small portions + where variables and small amounts of code need to be substituted. + Thus the idea was a sort of inverse of a Python interpreter: a + program that normally would just pass text through unmolested, but + when it found a special signifier would execute Python code in a + normal environment. I looked at existing Python templating + systems, and didn't find anything that appealed to me -- I wanted + something where the desired markups were simple and unobtrusive. + After considering between choices of signifiers, I settled on @ + and EmPy was born.

+

As I developed the tool, I realized it could have general appeal, + even to those with widely varying problems to solve, provided the + core tool they needed was an interpreter that could embed Python + code inside templated text. As I continue to use the tool, I have + been adding features as unintrusively as possible as I see areas + that can be improved.

+

A design goal of EmPy is that its feature set should work on + several levels; at each level, if the user does not wish or need + to use features from another level, they are under no obligation + to do so. If you have no need of diversions, for instance, you + are under no obligation to use them. If significators will not + help you organize a set of EmPy scripts globally, then you need + not use them. New features that are being added are whenever + possible transparently backward compatible; if you do not need + them, their introduction should not affect you in any way. The + use of unknown prefix sequences results in errors, guaranteeing + that they are reserved for future use.

+

Glossary

+
+
control
+
A control markup, used to direct high-level control + flow within an EmPy session. Control markups are expressed with + the @[...] notation.
+
diversion
+
A process by which output is deferred, and can be + recalled later on demand, multiple times if necessary.
+
document
+
The abstraction of an EmPy document as used by a + processor.
+
escape
+
A markup designed to expand to a single (usually + non-printable) character, similar to escape sequences in C or + other languages.
+
expansion
+
The process of processing EmPy markups and + producing output.
+
expression
+
An expression markup represents a Python + expression to be evaluated, and replaced with the str of its + value. Expression markups are expressed with the @(...) + notation.
+
filter
+
A file-like object which can be chained to other + objects (primarily the final stream) and can buffer, alter, or + manipulate in any way the data sent. Filters can also be + chained together in arbitrary order.
+
globals
+
The dictionary (or dictionary-like object) which + resides inside the interpreter and holds the currently-defined + variables.
+
hook
+
A callable object that can be registered in a + dictionary, and which will be invoked before, during, or after + certain internal operations, identified by name with a string.
+
interpreter
+
The application (or class instance) which + processes EmPy markup.
+
markup
+
EmPy substitutions set off with a prefix and + appropriate delimeters.
+
output
+
The final destination of the result of processing an + EmPy file.
+
prefix
+
The ASCII character used to set off an expansions. + By default, @.
+
processor
+
An extensible system which processes a group of + EmPy files, usually arranged in a filesystem, and scans them for + significators.
+
pseudomodule
+
The module-like object named empy which is + exposed internally inside every EmPy system.
+
shortcut
+
A special object which takes the place of an + instance of the Filter class, to represent a special form of + filter. These include 0 for a null filter, a callable (function + or lambda) to represent a callable filter, or a 256-character + string which represents a translation filter.
+
significator
+
A special form of an assignment markup in EmPy + which can be easily parsed externally, primarily designed for + representing uniform assignment across a collection of files. + Significators are indicated with the @% markup.
+
statement
+
A line of code that needs to be executed; + statements do not have return values. In EmPy, statements are + set off with @{...}.
+
+

Acknowledgements

+

Questions, suggestions, bug reports, evangelism, and even + complaints from many people have helped make EmPy what it is + today. Some, but by no means all, of these people are (in + alphabetical order by surname):

+ +
    +
  • Biswapesh Chattopadhyay

  • +
  • Beni Cherniavsky

  • +
  • Dr. S. Candelaria de Ram

  • +
  • Eric Eide

  • +
  • Dinu Gherman

  • +
  • Grzegorz Adam Hankiewicz

  • +
  • Bohdan Kushnir

  • +
  • Robert Kroeger

  • +
  • Kouichi Takahashi

  • +
  • Ville Vainio

  • + +
+

Known issues and caveats

+ +
    +
  • EmPy was primarily intended for static processing of documents, + rather than dynamic use, and hence speed of processing was not + the primary consideration in its design.

  • +
  • EmPy is not threadsafe by default. This is because of the need + for EmPy to override the sys.stdout file with a proxy object + which can capture effects of print and other spooling to + stdout. This proxy can be suppressed with the -n option, which + will result in EmPy being unable to do anything meaningful with + this output, but will allow EmPy to be threadsafe.

  • +
  • To function properly, EmPy must override sys.stdout with a + proxy file object, so that it can capture output of side effects + and support diversions for each interpreter instance. It is + important that code executed in an environment not rebind + sys.stdout, although it is perfectly legal to invoke it + explicitly (e.g., @sys.stdout.write("Hello world\n")). If + one really needs to access the "true" stdout, then use + sys.__stdout__ instead (which should also not be rebound). + EmPy uses the standard Python error handlers when exceptions are + raised in EmPy code, which print to sys.stderr.

  • +
  • Due to Python's curious handling of the print statement -- + particularly the form with a trailing comma to suppress the + final newline -- mixing statement expansions using prints inline + with unexpanded text will often result in surprising behavior, + such as extraneous (sometimes even deferred!) spaces. This is a + Python "feature," and occurs in non-EmPy applications as well; + for finer control over output formatting, use sys.stdout.write + or empy.interpreter.write directly.

  • +
  • The empy "module" exposed through the EmPy interface (e.g., + @empy) is an artificial module. It cannot be imported with + the import statement (and shouldn't -- it is an artifact of + the EmPy processing system and does not correspond to any + accessible .py file).

  • +
  • For an EmPy statement expansion all alone on a line, e.g., + @{a = 1}, note that this will expand to a blank line due to + the newline following the closing curly brace. To suppress this + blank line, use the symmetric convention @{a = 1}@.

  • +
  • When using EmPy with make, note that partial output may be + created before an error occurs; this is a standard caveat when + using make. To avoid this, write to a temporary file and move + when complete, delete the file in case of an error, use the -b + option to fully buffer output (including the open), or (with GNU + make) define a .DELETE_ON_ERROR target.

  • +
  • empy.identify tracks the context of executed EmPy code, not + Python code. This means that blocks of code delimited with @{ + and } will identify themselves as appearing on the line at + which the } appears, and that pure Python code executed via + the -D, -E and -F command line arguments will show up as all taking + place on line 1. If you're tracking errors and want more + information about the location of the errors from the Python + code, use the -r command line option, which will provide you + with the full Python traceback.

  • +
  • The conditional form of expression expansion @(...?...!...) + allows the use of a colon instead of an exclamation point, + e.g., @(...?...:...). This behavior is supported for + backward compatibility, but is deprecated. Due to an oversight, + the colon was a poor choice since colons can appear legally in + expressions (e.g., dictionary literals or lambda expressions).

  • +
  • The '@[try]' construct only works with Python exceptions derived + from Exception. It is not able to catch string exceptions.

  • +
  • The '@[for]' variable specification supports tuples for tuple + unpacking, even recursive tuples. However, it is limited in + that the names included may only be valid Python identifiers, + not arbitrary Python lvalues. Since the internal Python + mechanism is very rarely used for this purpose (e.g., 'for (x, + l[0], q.a) in sequence'), it is not thought to be a significant + limitation.

  • + +
+

Wish list

+

Here are some random ideas for future revisions of EmPy. If any + of these are of particular interest to you, your input would be + appreciated.

+ +
    +
  • Some real-world examples should really be included for + demonstrating the power and expressiveness of EmPy first-hand.

  • +
  • More extensive help (rather than a ridiculously long README), + probably inherently using the EmPy system itself for building to + HTML and other formats, thereby acting as a help facility and a + demonstration of the working system.

  • +
  • A "trivial" mode, where all the EmPy system does is scan for + simple symbols to replace them with evaluations/executions, + rather than having to do the contextual scanning it does now. + This has the down side of being much less configurable and + powerful but the upside of being extremely efficient.

  • +
  • A "debug" mode, where EmPy prints the contents of everything + it's about to evaluate (probably to stderr) before it does?

  • +
  • The ability to funnel all code through a configurable RExec + for user-controlled security control. This would probably + involve abstracting the execution functionality outside of the + interpreter. [This suggestion is on hold until the + rexec/Bastion exploits are worked out.]

  • +
  • Optimized handling of processing would be nice for the + possibility of an Apache module devoted to EmPy processing.

  • +
  • An EmPy emacs mode.

  • +
  • An optimization of offloading diversions to files when they + become truly huge. (This is made possible by the abstraction of + the Diversion class.)

  • +
  • Support for mapping filters (specified by dictionaries).

  • +
  • Support for some sort of batch processing, where several EmPy + files can be listed at once and all of them evaluated with the + same initial (presumably expensive) environment. + empy.saveGlobals and empy.restoreGlobals have been + introduced as a partial solution, but they need to be made more + robust.

  • +
  • A more elaborate interactive mode, perhaps with a prompt and + readline support.

  • +
  • A StructuredText and/or reStructuredText filter would be quite + useful, as would SGML/HTML/XML/XHTML, s-expression, Python, + etc. auto-indenter filters.

  • +
  • An indexing filter, which can process text and pick out + predefined keywords and thereby setup links to them.

  • +
  • The ability to rerun diverted material back through the + interpreter. (This can be done, awkwardly, by manually creating + a filter which itself contains an interpreter, but it might be + helpful if this was an all-in-one operation.)

  • +
  • A caching system that stores off the compilations of repeated + evaluations and executions so that in a persistent environment + the same code does not have to be repeatedly evaluated/executed. + This would probably be a necessity in an Apache module-based + solution. Perhaps caching even to the point of generating pure + PyWM bytecode?

  • +
  • An option to change the format of the standard EmPy errors in a + traceback.

  • +
  • Support for some manner of implicitly processed /etc/empyrc + and/or ~/.empyrc file, and of course an option to inhibit its + processing. This can already be accomplished (and with greater + control) via use of EMPY_OPTIONS, though.

  • +
  • More uniform handling of the preprocessing directives (-I, -D, + -E, -F, and -P), probably mapping directly to methods in the + Interpreter class.

  • +
  • Support for integration with mod_python.

  • +
  • In simple expressions, a {...} suffix has no meaning in Python + (e.g., in Python, @x(...) is a call, @x[...] is + subscription, but @x{...} is illegal). This could be + exploited by having a {...} suffix in a simple expression + representing an encapsulation of an expanded string; e.g., + @bullet{There are @count people here} would be equivalent to + @bullet(empy.expand("There are @count people here", + locals()))}.

  • +
  • A tool to collect significator information from a hierarchy of + .em files and put them in a database form available for + individual scripts would be extremely useful -- this tool should + be extensible so that users can use it to, say, build ordered + hierarchies of their EmPy files by detecting contextual + information like application-specific links to other EmPy + documents.

  • +
  • Extensions of the basic EmPy concepts to projects for other + interpreted languages, such as Java, Lua, Ruby, and/or Perl.

  • +
  • Ignore SystemExit when doing error handling, letting the + exception progagate up? So far no one seems to worry about + this; deliberately exiting early in a template seems to be an + unlikely occurrence. (Furthermore, there are the os.abort and + os._exit facilities for terminating without exception + propagation.)

  • +
  • A new markup which is the equivalent of $...:...$ in source + control systems, where the left-hand portion represents a + keyword and the right-hand portion represents its value which is + substituted in by the EmPy system.

  • +
  • The ability to obtain the filename (if relevant) and mode of the + primary output file.

  • +
  • The ability to redirect multiple streams of output; not + diversions, but rather the ability to write to one file and then + another. Since output would be under the EmPy script's control, + this would imply a useful --no-output option, where by default + no output is written. This would also suggest the usefulness of + all the output file delegates (diversions, filters, abstract + files, etc.) passing unrecognized method calls all the way down + to underlying file object.

  • +
  • In addition to the em.py script, an additional support library + (non-executable) should be included which includes ancillary + functionality for more advanced features, but which is not + necessary to use EmPy in its basic form as a standalone + executable. Such features would include things like + significator processing, metadata scanning, and advanced + prompting systems.

  • + +
+

Release history

+ +
    +
  • 3.3.2; 2014 Jan 24. Additional fix for source compatibility + between 2.x and 3.0.

  • +
  • 3.3.1; 2014 Jan 22. Source compatibility for 2.x and 3.0; + 1.x and Jython compatibility dropped. +

  • 3.3; 2003 Oct 27. Custom markup '@<...>'; remove separate + pseudomodule instance for greater transparency; deprecate + interpreter attribute of pseudomodule; deprecate auxiliary + class name attributes associated with pseudomodule in + preparation for separate support library in 4.0; add + --no-callback-error and --no-bangpath-processing command line + options; add atToken hook.

  • +
  • 3.2; 2003 Oct 7. Reengineer hooks support to use hook + instances; add -v option; add --relative-path option; reversed + PEP 317 style; modify Unicode support to give less confusing + errors in the case of unknown encodings and error handlers; + relicensed under LGPL.

  • +
  • 3.1.1; 2003 Sep 20. Add literal @"..." markup; add + --pause-at-end command line option; fix improper globals + collision error via the sys.stdout proxy.

  • +
  • 3.1; 2003 Aug 8. Unicode support (Python 2.0 and above); add + Document and Processor helper classes for processing + significators; add --no-prefix option for suppressing all + markups.

  • +
  • 3.0.4; 2003 Aug 7. Implement somewhat more robust lvalue + parsing for '@[for]' construct (thanks to Beni Cherniavsky for + inspiration).

  • +
  • 3.0.3; 2003 Jul 9. Fix bug regarding recursive tuple unpacking + using '@[for]'; add empy.saveGlobals, empy.restoreGlobals, + and empy.defined functions.

  • +
  • 3.0.2; 2003 Jun 19. @? and @! markups for changing the + current context name and line, respectively; add update method + to interpreter; new and renamed context operations, + empy.setContextName, empy.setContextLine, + empy.pushContext, empy.popContext.

  • +
  • 3.0.1; 2003 Jun 9. Fix simple bug preventing command line + preprocessing directives (-I, -D, -E, -F, -P) from executing + properly; defensive PEP 317 compliance [defunct].

  • +
  • 3.0; 2003 Jun 1. Control markups with '@[...]'; remove + substitutions (use control markups instead); support + @(...?...!...) for conditional expressions in addition to the + now-deprecated @(...?...:...) variety; add acknowledgements + and glossary sections to documentation; rename buffering option + back to -b; add -m option and EMPY_PSEUDO environment variable + for changing the pseudomodule name; add -n option and + EMPY_NO_OVERRIDE environment variable for suppressing + sys.stdout proxy; rename main error class to 'Error'; add + standalone expand function; add --binary and --chunk-size + options; reengineer parsing system to use Tokens for easy + extensibility; safeguard curly braces in simple expressions + (meaningless in Python and thus likely a typographical error) by + making them a parse error; fix bug involving custom Interpreter + instances ignoring globals argument; distutils support.

  • +
  • 2.3; 2003 Feb 20. Proper and full support for concurrent and + recursive interpreters; protection from closing the true stdout + file object; detect edge cases of interpreter globals or + sys.stdout proxy collisions; add globals manipulation + functions empy.getGlobals, empy.setGlobals, and + empy.updateGlobals which properly preserve the empy + pseudomodule; separate usage info out into easily accessible + lists for easier presentation; have -h option show simple usage + and -H show extened usage; add NullFile utility class.

  • +
  • 2.2.6; 2003 Jan 30. Fix a bug in the Filter.detach method + (which would not normally be called anyway).

  • +
  • 2.2.5; 2003 Jan 9. Strip carriage returns out of executed code + blocks for DOS/Windows compatibility.

  • +
  • 2.2.4; 2002 Dec 23. Abstract Filter interface to use methods + only; add @[noop: ...] substitution for completeness and block + commenting [defunct].

  • +
  • 2.2.3; 2002 Dec 16. Support compatibility with Jython by + working around a minor difference between CPython and Jython in + string splitting.

  • +
  • 2.2.2; 2002 Dec 14. Include better docstrings for pseudomodule + functions; segue to a dictionary-based options system for + interpreters; add empy.clearAllHooks and 'empy.clearGlobals'; + include a short documentation section on embedding interpreters; + fix a bug in significator regular expression.

  • +
  • 2.2.1; 2002 Nov 30. Tweak test script to avoid writing + unnecessary temporary file; add Interpreter.single method; + expose evaluate, execute, substitute [defunct], and + single methods to the pseudomodule; add (rather obvious) + EMPY_OPTIONS environment variable support; add + empy.enableHooks and 'empy.disableHooks'; include optimization + to transparently disable hooks until they are actually used.

  • +
  • 2.2; 2002 Nov 21. Switched to -V option for version + information; empy.createDiversion for creating initially empty + diversion; direct access to diversion objects with + 'empy.retrieveDiversion'; environment variable support; removed + --raw long argument (use --raw-errors instead); added quaternary + escape code (well, why not).

  • +
  • 2.1; 2002 Oct 18. empy.atExit registry separate from hooks to + allow for normal interpreter support; include a benchmark sample + and test.sh verification script; expose empy.string directly; + -D option for explicit defines on command line; remove + ill-conceived support for @else: separator in @[if ...] + substitution [defunct] ; handle nested substitutions properly + [defunct] ; @[macro ...] substitution for creating recallable + expansions [defunct].

  • +
  • 2.0.1; 2002 Oct 8. Fix missing usage information; fix + after_evaluate hook not getting called; add empy.atExit call + to register values.

  • +
  • 2.0; 2002 Sep 30. Parsing system completely revamped and + simplified, eliminating a whole class of context-related bugs; + builtin support for buffered filters; support for registering + hooks; support for command line arguments; interactive mode with + -i; significator value extended to be any valid Python + expression.

  • +
  • 1.5.1; 2002 Sep 24. Allow @] to represent unbalanced close + brackets in @[...] markups [defunct].

  • +
  • 1.5; 2002 Sep 18. Escape codes (@\...); conditional and + repeated expansion substitutions [defunct] ; replaced with control + markups]; fix a few bugs involving files which do not end in + newlines.

  • +
  • 1.4; 2002 Sep 7. Fix bug with triple quotes; collapse + conditional and protected expression syntaxes into the single + generalized @(...) notation; empy.setName and empy.setLine + functions [deprecated] ; true support for multiple concurrent + interpreters with improved sys.stdout proxy; proper support for + empy.expand to return a string evaluated in a subinterpreter + as intended; merged Context and Parser classes together, and + separated out Scanner functionality.

  • +
  • 1.3; 2002 Aug 24. Pseudomodule as true instance; move toward + more verbose (and clear) pseudomodule functions; fleshed out + diversion model; filters; conditional expressions; protected + expressions; preprocessing with -P (in preparation for + possible support for command line arguments).

  • +
  • 1.2; 2002 Aug 16. Treat bangpaths as comments; empy.quote for + the opposite process of 'empy.expand'; significators (@%... + sequences); -I option; -f option; much improved documentation.

  • +
  • 1.1.5; 2002 Aug 15. Add a separate invoke function that can be + called multiple times with arguments to simulate multiple runs.

  • +
  • 1.1.4; 2002 Aug 12. Handle strings thrown as exceptions + properly; use getopt to process command line arguments; cleanup + file buffering with AbstractFile; very slight documentation and + code cleanup.

  • +
  • 1.1.3; 2002 Aug 9. Support for changing the prefix from within + the empy pseudomodule.

  • +
  • 1.1.2; 2002 Aug 5. Renamed buffering option [defunct], added -F + option for interpreting Python files from the command line, + fixed improper handling of exceptions from command line options + (-E, -F).

  • +
  • 1.1.1; 2002 Aug 4. Typo bugfixes; documentation clarification.

  • +
  • 1.1; 2002 Aug 4. Added option for fully buffering output + (including file opens), executing commands through the command + line; some documentation errors fixed.

  • +
  • 1.0; 2002 Jul 23. Renamed project to EmPy. Documentation and + sample tweaks; added empy.flatten. Added -a option.

  • +
  • 0.3; 2002 Apr 14. Extended "simple expression" syntax, + interpreter abstraction, proper context handling, better error + handling, explicit file inclusion, extended samples.

  • +
  • 0.2; 2002 Apr 13. Bugfixes, support non-expansion of Nones, + allow choice of alternate prefix.

  • +
  • 0.1.1; 2002 Apr 12. Bugfixes, support for Python 1.5.x, add -r + option.

  • +
  • 0.1; 2002 Apr 12. Initial early access release.

  • + +
+

Author

+

This module was written by Erik Max Francis. If you use this software, have + suggestions for future releases, or bug reports, I'd love to hear + about it.

+

Even if you try out EmPy for a project and find it unsuitable, I'd + like to know what stumbling blocks you ran into so they can + potentially be addressed in a future version.

+

Version

+

Version 3.3.2 $Date: 2004-01-25 $ $Author: max $

+ + + + + + + + +
+ + Modules and Packages  + + +   +
+ + + +

em

+

A system for processing Python as markup embedded in text.

+
+
+ +
+ +
+ +

Table of Contents

+ + This document was automatically generated + on Wed Jan 22 00:00:00 2014 by + HappyDoc version + 2.1 + + + + \ No newline at end of file diff --git a/em.py b/em.py new file mode 100755 index 0000000..73e5650 --- /dev/null +++ b/em.py @@ -0,0 +1,3302 @@ +#!/usr/bin/env python +# +# $Id: em.py 5364 2014-01-24 21:39:38Z max $ $Date: 2014-01-24 13:39:38 -0800 (Fri, 24 Jan 2014) $ + +""" +A system for processing Python as markup embedded in text. +""" + + +__program__ = 'empy' +__version__ = '3.3.2' +__url__ = 'http://www.alcyone.com/software/empy/' +__author__ = 'Erik Max Francis ' +__copyright__ = 'Copyright (C) 2002-2014 Erik Max Francis' +__license__ = 'LGPL' + + +import copy +import getopt +import inspect +import os +import re +import sys +import types + +# 2.x/3.0 compatbility +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +try: + _unicode = unicode # bytes will be undefined in 3.x releases + _str = str + _unichr = unichr + _input = raw_input + def _exec(code, globals, locals=None): + if globals is None: + exec("""exec code""") + else: + if locals is None: + exec("""exec code in globals""") + else: + exec("""exec code in globals, locals""") +except NameError: + _unicode = str + _str = bytes + _unichr = chr + _input = input + try: + _exec = __builtins__.__dict__['exec'] + except AttributeError: + _exec = __builtins__['exec'] + +# Some basic defaults. +FAILURE_CODE = 1 +DEFAULT_PREFIX = '@' +DEFAULT_PSEUDOMODULE_NAME = 'empy' +DEFAULT_SCRIPT_NAME = '?' +SIGNIFICATOR_RE_SUFFIX = r"%(\S+)\s*(.*)\s*$" +SIGNIFICATOR_RE_STRING = DEFAULT_PREFIX + SIGNIFICATOR_RE_SUFFIX +BANGPATH = '#!' +DEFAULT_CHUNK_SIZE = 8192 +DEFAULT_ERRORS = 'strict' + +# Character information. +IDENTIFIER_FIRST_CHARS = '_abcdefghijklmnopqrstuvwxyz' \ + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +IDENTIFIER_CHARS = IDENTIFIER_FIRST_CHARS + '0123456789.' +ENDING_CHARS = {'(': ')', '[': ']', '{': '}'} + +# Environment variable names. +OPTIONS_ENV = 'EMPY_OPTIONS' +PREFIX_ENV = 'EMPY_PREFIX' +PSEUDO_ENV = 'EMPY_PSEUDO' +FLATTEN_ENV = 'EMPY_FLATTEN' +RAW_ENV = 'EMPY_RAW_ERRORS' +INTERACTIVE_ENV = 'EMPY_INTERACTIVE' +BUFFERED_ENV = 'EMPY_BUFFERED_OUTPUT' +NO_OVERRIDE_ENV = 'EMPY_NO_OVERRIDE' +UNICODE_ENV = 'EMPY_UNICODE' +INPUT_ENCODING_ENV = 'EMPY_UNICODE_INPUT_ENCODING' +OUTPUT_ENCODING_ENV = 'EMPY_UNICODE_OUTPUT_ENCODING' +INPUT_ERRORS_ENV = 'EMPY_UNICODE_INPUT_ERRORS' +OUTPUT_ERRORS_ENV = 'EMPY_UNICODE_OUTPUT_ERRORS' + +# Interpreter options. +BANGPATH_OPT = 'processBangpaths' # process bangpaths as comments? +BUFFERED_OPT = 'bufferedOutput' # fully buffered output? +RAW_OPT = 'rawErrors' # raw errors? +EXIT_OPT = 'exitOnError' # exit on error? +FLATTEN_OPT = 'flatten' # flatten pseudomodule namespace? +OVERRIDE_OPT = 'override' # override sys.stdout with proxy? +CALLBACK_OPT = 'noCallbackError' # is no custom callback an error? + +# Usage info. +OPTION_INFO = [ +("-V --version", "Print version and exit"), +("-h --help", "Print usage and exit"), +("-H --extended-help", "Print extended usage and exit"), +("-k --suppress-errors", "Do not exit on errors; go interactive"), +("-p --prefix=", "Change prefix to something other than @"), +(" --no-prefix", "Do not do any markup processing at all"), +("-m --module=", "Change the internal pseudomodule name"), +("-f --flatten", "Flatten the members of pseudmodule to start"), +("-r --raw-errors", "Show raw Python errors"), +("-i --interactive", "Go into interactive mode after processing"), +("-n --no-override-stdout", "Do not override sys.stdout with proxy"), +("-o --output=", "Specify file for output as write"), +("-a --append=", "Specify file for output as append"), +("-b --buffered-output", "Fully buffer output including open"), +(" --binary", "Treat the file as a binary"), +(" --chunk-size=", "Use this chunk size for reading binaries"), +("-P --preprocess=", "Interpret EmPy file before main processing"), +("-I --import=", "Import Python modules before processing"), +("-D --define=", "Execute Python assignment statement"), +("-E --execute=", "Execute Python statement before processing"), +("-F --execute-file=", "Execute Python file before processing"), +(" --pause-at-end", "Prompt at the ending of processing"), +(" --relative-path", "Add path of EmPy script to sys.path"), +(" --no-callback-error", "Custom markup without callback is error"), +(" --no-bangpath-processing", "Suppress bangpaths as comments"), +("-u --unicode", "Enable Unicode subsystem (Python 2+ only)"), +(" --unicode-encoding=", "Set both input and output encodings"), +(" --unicode-input-encoding=", "Set input encoding"), +(" --unicode-output-encoding=", "Set output encoding"), +(" --unicode-errors=", "Set both input and output error handler"), +(" --unicode-input-errors=", "Set input error handler"), +(" --unicode-output-errors=", "Set output error handler"), +] + +USAGE_NOTES = """\ +Notes: Whitespace immediately inside parentheses of @(...) are +ignored. Whitespace immediately inside braces of @{...} are ignored, +unless ... spans multiple lines. Use @{ ... }@ to suppress newline +following expansion. Simple expressions ignore trailing dots; `@x.' +means `@(x).'. A #! at the start of a file is treated as a @# +comment.""" + +MARKUP_INFO = [ +("@# ... NL", "Comment; remove everything up to newline"), +("@? NAME NL", "Set the current context name"), +("@! INTEGER NL", "Set the current context line number"), +("@ WHITESPACE", "Remove following whitespace; line continuation"), +("@\\ ESCAPE_CODE", "A C-style escape sequence"), +("@@", "Literal @; @ is escaped (duplicated prefix)"), +("@), @], @}", "Literal close parenthesis, bracket, brace"), +("@ STRING_LITERAL", "Replace with string literal contents"), +("@( EXPRESSION )", "Evaluate expression and substitute with str"), +("@( TEST [? THEN [! ELSE]] )", "If test is true, evaluate then, otherwise else"), +("@( TRY $ CATCH )", "Expand try expression, or catch if it raises"), +("@ SIMPLE_EXPRESSION", "Evaluate simple expression and substitute;\n" + "e.g., @x, @x.y, @f(a, b), @l[i], etc."), +("@` EXPRESSION `", "Evaluate expression and substitute with repr"), +("@: EXPRESSION : [DUMMY] :", "Evaluates to @:...:expansion:"), +("@{ STATEMENTS }", "Statements are executed for side effects"), +("@[ CONTROL ]", "Control markups: if E; elif E; for N in E;\n" + "while E; try; except E, N; finally; continue;\n" + "break; end X"), +("@%% KEY WHITESPACE VALUE NL", "Significator form of __KEY__ = VALUE"), +("@< CONTENTS >", "Custom markup; meaning provided by user"), +] + +ESCAPE_INFO = [ +("@\\0", "NUL, null"), +("@\\a", "BEL, bell"), +("@\\b", "BS, backspace"), +("@\\dDDD", "three-digit decimal code DDD"), +("@\\e", "ESC, escape"), +("@\\f", "FF, form feed"), +("@\\h", "DEL, delete"), +("@\\n", "LF, linefeed, newline"), +("@\\N{NAME}", "Unicode character named NAME"), +("@\\oOOO", "three-digit octal code OOO"), +("@\\qQQQQ", "four-digit quaternary code QQQQ"), +("@\\r", "CR, carriage return"), +("@\\s", "SP, space"), +("@\\t", "HT, horizontal tab"), +("@\\uHHHH", "16-bit hexadecimal Unicode HHHH"), +("@\\UHHHHHHHH", "32-bit hexadecimal Unicode HHHHHHHH"), +("@\\v", "VT, vertical tab"), +("@\\xHH", "two-digit hexadecimal code HH"), +("@\\z", "EOT, end of transmission"), +] + +PSEUDOMODULE_INFO = [ +("VERSION", "String representing EmPy version"), +("SIGNIFICATOR_RE_STRING", "Regular expression matching significators"), +("SIGNIFICATOR_RE_SUFFIX", "The above stub, lacking the prefix"), +("interpreter", "Currently-executing interpreter instance"), +("argv", "The EmPy script name and command line arguments"), +("args", "The command line arguments only"), +("identify()", "Identify top context as name, line"), +("setContextName(name)", "Set the name of the current context"), +("setContextLine(line)", "Set the line number of the current context"), +("atExit(callable)", "Invoke no-argument function at shutdown"), +("getGlobals()", "Retrieve this interpreter's globals"), +("setGlobals(dict)", "Set this interpreter's globals"), +("updateGlobals(dict)", "Merge dictionary into interpreter's globals"), +("clearGlobals()", "Start globals over anew"), +("saveGlobals([deep])", "Save a copy of the globals"), +("restoreGlobals([pop])", "Restore the most recently saved globals"), +("defined(name, [loc])", "Find if the name is defined"), +("evaluate(expression, [loc])", "Evaluate the expression"), +("serialize(expression, [loc])", "Evaluate and serialize the expression"), +("execute(statements, [loc])", "Execute the statements"), +("single(source, [loc])", "Execute the 'single' object"), +("atomic(name, value, [loc])", "Perform an atomic assignment"), +("assign(name, value, [loc])", "Perform an arbitrary assignment"), +("significate(key, [value])", "Significate the given key, value pair"), +("include(file, [loc])", "Include filename or file-like object"), +("expand(string, [loc])", "Explicitly expand string and return"), +("string(data, [name], [loc])", "Process string-like object"), +("quote(string)", "Quote prefixes in provided string and return"), +("flatten([keys])", "Flatten module contents into globals namespace"), +("getPrefix()", "Get current prefix"), +("setPrefix(char)", "Set new prefix"), +("stopDiverting()", "Stop diverting; data sent directly to output"), +("createDiversion(name)", "Create a diversion but do not divert to it"), +("retrieveDiversion(name)", "Retrieve the actual named diversion object"), +("startDiversion(name)", "Start diverting to given diversion"), +("playDiversion(name)", "Recall diversion and then eliminate it"), +("replayDiversion(name)", "Recall diversion but retain it"), +("purgeDiversion(name)", "Erase diversion"), +("playAllDiversions()", "Stop diverting and play all diversions in order"), +("replayAllDiversions()", "Stop diverting and replay all diversions"), +("purgeAllDiversions()", "Stop diverting and purge all diversions"), +("getFilter()", "Get current filter"), +("resetFilter()", "Reset filter; no filtering"), +("nullFilter()", "Install null filter"), +("setFilter(shortcut)", "Install new filter or filter chain"), +("attachFilter(shortcut)", "Attach single filter to end of current chain"), +("areHooksEnabled()", "Return whether or not hooks are enabled"), +("enableHooks()", "Enable hooks (default)"), +("disableHooks()", "Disable hook invocation"), +("getHooks()", "Get all the hooks"), +("clearHooks()", "Clear all hooks"), +("addHook(hook, [i])", "Register the hook (optionally insert)"), +("removeHook(hook)", "Remove an already-registered hook from name"), +("invokeHook(name_, ...)", "Manually invoke hook"), +("getCallback()", "Get interpreter callback"), +("registerCallback(callback)", "Register callback with interpreter"), +("deregisterCallback()", "Deregister callback from interpreter"), +("invokeCallback(contents)", "Invoke the callback directly"), +("Interpreter", "The interpreter class"), +] + +ENVIRONMENT_INFO = [ +(OPTIONS_ENV, "Specified options will be included"), +(PREFIX_ENV, "Specify the default prefix: -p "), +(PSEUDO_ENV, "Specify name of pseudomodule: -m "), +(FLATTEN_ENV, "Flatten empy pseudomodule if defined: -f"), +(RAW_ENV, "Show raw errors if defined: -r"), +(INTERACTIVE_ENV, "Enter interactive mode if defined: -i"), +(BUFFERED_ENV, "Fully buffered output if defined: -b"), +(NO_OVERRIDE_ENV, "Do not override sys.stdout if defined: -n"), +(UNICODE_ENV, "Enable Unicode subsystem: -n"), +(INPUT_ENCODING_ENV, "Unicode input encoding"), +(OUTPUT_ENCODING_ENV, "Unicode output encoding"), +(INPUT_ERRORS_ENV, "Unicode input error handler"), +(OUTPUT_ERRORS_ENV, "Unicode output error handler"), +] + +class Error(Exception): + """The base class for all EmPy errors.""" + pass + +EmpyError = EmPyError = Error # DEPRECATED + +class DiversionError(Error): + """An error related to diversions.""" + pass + +class FilterError(Error): + """An error related to filters.""" + pass + +class StackUnderflowError(Error): + """A stack underflow.""" + pass + +class SubsystemError(Error): + """An error associated with the Unicode subsystem.""" + pass + +class FlowError(Error): + """An exception related to control flow.""" + pass + +class ContinueFlow(FlowError): + """A continue control flow.""" + pass + +class BreakFlow(FlowError): + """A break control flow.""" + pass + +class ParseError(Error): + """A parse error occurred.""" + pass + +class TransientParseError(ParseError): + """A parse error occurred which may be resolved by feeding more data. + Such an error reaching the toplevel is an unexpected EOF error.""" + pass + + +class MetaError(Exception): + + """A wrapper around a real Python exception for including a copy of + the context.""" + + def __init__(self, contexts, exc): + Exception.__init__(self, exc) + self.contexts = contexts + self.exc = exc + + def __str__(self): + backtrace = [str(x) for x in self.contexts] + return "%s: %s (%s)" % (self.exc.__class__, self.exc, + (', '.join(backtrace))) + + +class Subsystem: + + """The subsystem class defers file creation so that it can create + Unicode-wrapped files if desired (and possible).""" + + def __init__(self): + self.useUnicode = False + self.inputEncoding = None + self.outputEncoding = None + self.errors = None + + def initialize(self, inputEncoding=None, outputEncoding=None, + inputErrors=None, outputErrors=None): + self.useUnicode = True + defaultEncoding = sys.getdefaultencoding() + if inputEncoding is None: + inputEncoding = defaultEncoding + self.inputEncoding = inputEncoding + if outputEncoding is None: + outputEncoding = defaultEncoding + self.outputEncoding = outputEncoding + if inputErrors is None: + inputErrors = DEFAULT_ERRORS + self.inputErrors = inputErrors + if outputErrors is None: + outputErrors = DEFAULT_ERRORS + self.outputErrors = outputErrors + + def assertUnicode(self): + if not self.useUnicode: + raise SubsystemError("Unicode subsystem unavailable") + + def open(self, name, mode=None): + if self.useUnicode: + return self.unicodeOpen(name, mode) + else: + return self.defaultOpen(name, mode) + + def defaultOpen(self, name, mode=None): + if mode is None: + mode = 'r' + return open(name, mode) + + def unicodeOpen(self, name, mode=None): + import codecs + if mode is None: + mode = 'rb' + if mode.find('w') >= 0 or mode.find('a') >= 0: + encoding = self.outputEncoding + errors = self.outputErrors + else: + encoding = self.inputEncoding + errors = self.inputErrors + return codecs.open(name, mode, encoding, errors) + +theSubsystem = Subsystem() + + +class Stack: + + """A simple stack that behaves as a sequence (with 0 being the top + of the stack, not the bottom).""" + + def __init__(self, seq=None): + if seq is None: + seq = [] + self.data = seq + + def top(self): + """Access the top element on the stack.""" + try: + return self.data[-1] + except IndexError: + raise StackUnderflowError("stack is empty for top") + + def pop(self): + """Pop the top element off the stack and return it.""" + try: + return self.data.pop() + except IndexError: + raise StackUnderflowError("stack is empty for pop") + + def push(self, object): + """Push an element onto the top of the stack.""" + self.data.append(object) + + def filter(self, function): + """Filter the elements of the stack through the function.""" + self.data = list(filter(function, self.data)) + + def purge(self): + """Purge the stack.""" + self.data = [] + + def clone(self): + """Create a duplicate of this stack.""" + return self.__class__(self.data[:]) + + def __nonzero__(self): return len(self.data) != 0 # 2.x + def __bool__(self): return len(self.data) != 0 # 3.x + def __len__(self): return len(self.data) + def __getitem__(self, index): return self.data[-(index + 1)] + + def __repr__(self): + return ('<%s instance at 0x%x [%s]>' % + (self.__class__, id(self), + ', '.join(repr(x) for x in self.data))) + + +class AbstractFile: + + """An abstracted file that, when buffered, will totally buffer the + file, including even the file open.""" + + def __init__(self, filename, mode='w', buffered=False): + # The calls below might throw, so start off by marking this + # file as "done." This way destruction of a not-completely- + # initialized AbstractFile will generate no further errors. + self.done = True + self.filename = filename + self.mode = mode + self.buffered = buffered + if buffered: + self.bufferFile = StringIO() + else: + self.bufferFile = theSubsystem.open(filename, mode) + # Okay, we got this far, so the AbstractFile is initialized. + # Flag it as "not done." + self.done = False + + def __del__(self): + self.close() + + def write(self, data): + self.bufferFile.write(data) + + def writelines(self, data): + self.bufferFile.writelines(data) + + def flush(self): + self.bufferFile.flush() + + def close(self): + if not self.done: + self.commit() + self.done = True + + def commit(self): + if self.buffered: + file = theSubsystem.open(self.filename, self.mode) + file.write(self.bufferFile.getvalue()) + file.close() + else: + self.bufferFile.close() + + def abort(self): + if self.buffered: + self.bufferFile = None + else: + self.bufferFile.close() + self.bufferFile = None + self.done = True + + +class Diversion: + + """The representation of an active diversion. Diversions act as + (writable) file objects, and then can be recalled either as pure + strings or (readable) file objects.""" + + def __init__(self): + self.file = StringIO() + + # These methods define the writable file-like interface for the + # diversion. + + def write(self, data): + self.file.write(data) + + def writelines(self, lines): + for line in lines: + self.write(line) + + def flush(self): + self.file.flush() + + def close(self): + self.file.close() + + # These methods are specific to diversions. + + def asString(self): + """Return the diversion as a string.""" + return self.file.getvalue() + + def asFile(self): + """Return the diversion as a file.""" + return StringIO(self.file.getvalue()) + + +class Stream: + + """A wrapper around an (output) file object which supports + diversions and filtering.""" + + def __init__(self, file): + self.file = file + self.currentDiversion = None + self.diversions = {} + self.filter = file + self.done = False + + def write(self, data): + if self.currentDiversion is None: + self.filter.write(data) + else: + self.diversions[self.currentDiversion].write(data) + + def writelines(self, lines): + for line in lines: + self.write(line) + + def flush(self): + self.filter.flush() + + def close(self): + if not self.done: + self.undivertAll(True) + self.filter.close() + self.done = True + + def shortcut(self, shortcut): + """Take a filter shortcut and translate it into a filter, returning + it. Sequences don't count here; these should be detected + independently.""" + if shortcut == 0: + return NullFilter() + elif (isinstance(shortcut, types.FunctionType) or + inspect.ismethoddescriptor(shortcut) or + isinstance(shortcut, types.BuiltinFunctionType) or + isinstance(shortcut, types.BuiltinMethodType) or + isinstance(shortcut, types.LambdaType)): + return FunctionFilter(shortcut) + elif isinstance(shortcut, _str) or isinstance(shortcut, _unicode): + return StringFilter(filter) + elif isinstance(shortcut, dict): + raise NotImplementedError("mapping filters not yet supported") + else: + # Presume it's a plain old filter. + return shortcut + + def last(self): + """Find the last filter in the current filter chain, or None if + there are no filters installed.""" + if self.filter is None: + return None + thisFilter, lastFilter = self.filter, None + while thisFilter is not None and thisFilter is not self.file: + lastFilter = thisFilter + thisFilter = thisFilter.next() + return lastFilter + + def install(self, shortcut=None): + """Install a new filter; None means no filter. Handle all the + special shortcuts for filters here.""" + # Before starting, execute a flush. + self.filter.flush() + if shortcut is None or shortcut == [] or shortcut == (): + # Shortcuts for "no filter." + self.filter = self.file + else: + if isinstance(shortcut, list) or isinstance(shortcut, tuple): + shortcuts = list(shortcut) + else: + shortcuts = [shortcut] + # Run through the shortcut filter names, replacing them with + # full-fledged instances of Filter. + filters = [] + for shortcut in shortcuts: + filters.append(self.shortcut(shortcut)) + if len(filters) > 1: + # If there's more than one filter provided, chain them + # together. + lastFilter = None + for filter in filters: + if lastFilter is not None: + lastFilter.attach(filter) + lastFilter = filter + lastFilter.attach(self.file) + self.filter = filters[0] + else: + # If there's only one filter, assume that it's alone or it's + # part of a chain that has already been manually chained; + # just find the end. + filter = filters[0] + lastFilter = filter.last() + lastFilter.attach(self.file) + self.filter = filter + + def attach(self, shortcut): + """Attached a solitary filter (no sequences allowed here) at the + end of the current filter chain.""" + lastFilter = self.last() + if lastFilter is None: + # Just install it from scratch if there is no active filter. + self.install(shortcut) + else: + # Attach the last filter to this one, and this one to the file. + filter = self.shortcut(shortcut) + lastFilter.attach(filter) + filter.attach(self.file) + + def revert(self): + """Reset any current diversions.""" + self.currentDiversion = None + + def create(self, name): + """Create a diversion if one does not already exist, but do not + divert to it yet.""" + if name is None: + raise DiversionError("diversion name must be non-None") + if name not in self.diversions: + self.diversions[name] = Diversion() + + def retrieve(self, name): + """Retrieve the given diversion.""" + if name is None: + raise DiversionError("diversion name must be non-None") + if name in self.diversions: + return self.diversions[name] + else: + raise DiversionError("nonexistent diversion: %s" % name) + + def divert(self, name): + """Start diverting.""" + if name is None: + raise DiversionError("diversion name must be non-None") + self.create(name) + self.currentDiversion = name + + def undivert(self, name, purgeAfterwards=False): + """Undivert a particular diversion.""" + if name is None: + raise DiversionError("diversion name must be non-None") + if name in self.diversions: + diversion = self.diversions[name] + self.filter.write(diversion.asString()) + if purgeAfterwards: + self.purge(name) + else: + raise DiversionError("nonexistent diversion: %s" % name) + + def purge(self, name): + """Purge the specified diversion.""" + if name is None: + raise DiversionError("diversion name must be non-None") + if name in self.diversions: + del self.diversions[name] + if self.currentDiversion == name: + self.currentDiversion = None + + def undivertAll(self, purgeAfterwards=True): + """Undivert all pending diversions.""" + if self.diversions: + self.revert() # revert before undiverting! + names = sorted(self.diversions.keys()) + for name in names: + self.undivert(name) + if purgeAfterwards: + self.purge(name) + + def purgeAll(self): + """Eliminate all existing diversions.""" + if self.diversions: + self.diversions = {} + self.currentDiversion = None + + +class NullFile: + + """A simple class that supports all the file-like object methods + but simply does nothing at all.""" + + def __init__(self): pass + def write(self, data): pass + def writelines(self, lines): pass + def flush(self): pass + def close(self): pass + + +class UncloseableFile: + + """A simple class which wraps around a delegate file-like object + and lets everything through except close calls.""" + + def __init__(self, delegate): + self.delegate = delegate + + def write(self, data): + self.delegate.write(data) + + def writelines(self, lines): + self.delegate.writelines(data) + + def flush(self): + self.delegate.flush() + + def close(self): + """Eat this one.""" + pass + + +class ProxyFile: + + """The proxy file object that is intended to take the place of + sys.stdout. The proxy can manage a stack of file objects it is + writing to, and an underlying raw file object.""" + + def __init__(self, bottom): + self.stack = Stack() + self.bottom = bottom + + def current(self): + """Get the current stream to write to.""" + if self.stack: + return self.stack[-1][1] + else: + return self.bottom + + def push(self, interpreter): + self.stack.push((interpreter, interpreter.stream())) + + def pop(self, interpreter): + result = self.stack.pop() + assert interpreter is result[0] + + def clear(self, interpreter): + self.stack.filter(lambda x, i=interpreter: x[0] is not i) + + def write(self, data): + self.current().write(data) + + def writelines(self, lines): + self.current().writelines(lines) + + def flush(self): + self.current().flush() + + def close(self): + """Close the current file. If the current file is the bottom, then + close it and dispose of it.""" + current = self.current() + if current is self.bottom: + self.bottom = None + current.close() + + def _testProxy(self): pass + + +class Filter: + + """An abstract filter.""" + + def __init__(self): + if self.__class__ is Filter: + raise NotImplementedError + self.sink = None + + def next(self): + """Return the next filter/file-like object in the sequence, or None.""" + return self.sink + + def __next__(self): return self.next() + + def write(self, data): + """The standard write method; this must be overridden in subclasses.""" + raise NotImplementedError + + def writelines(self, lines): + """Standard writelines wrapper.""" + for line in lines: + self.write(line) + + def _flush(self): + """The _flush method should always flush the sink and should not + be overridden.""" + self.sink.flush() + + def flush(self): + """The flush method can be overridden.""" + self._flush() + + def close(self): + """Close the filter. Do an explicit flush first, then close the + sink.""" + self.flush() + self.sink.close() + + def attach(self, filter): + """Attach a filter to this one.""" + if self.sink is not None: + # If it's already attached, detach it first. + self.detach() + self.sink = filter + + def detach(self): + """Detach a filter from its sink.""" + self.flush() + self._flush() # do a guaranteed flush to just to be safe + self.sink = None + + def last(self): + """Find the last filter in this chain.""" + this, last = self, self + while this is not None: + last = this + this = this.next() + return last + +class NullFilter(Filter): + + """A filter that never sends any output to its sink.""" + + def write(self, data): pass + +class FunctionFilter(Filter): + + """A filter that works simply by pumping its input through a + function which maps strings into strings.""" + + def __init__(self, function): + Filter.__init__(self) + self.function = function + + def write(self, data): + self.sink.write(self.function(data)) + +class StringFilter(Filter): + + """A filter that takes a translation string (256 characters) and + filters any incoming data through it.""" + + def __init__(self, table): + if not ((isinstance(table, _str) or isinstance(table, _unicode)) + and len(table) == 256): + raise FilterError("table must be 256-character string") + Filter.__init__(self) + self.table = table + + def write(self, data): + self.sink.write(data.translate(self.table)) + +class BufferedFilter(Filter): + + """A buffered filter is one that doesn't modify the source data + sent to the sink, but instead holds it for a time. The standard + variety only sends the data along when it receives a flush + command.""" + + def __init__(self): + Filter.__init__(self) + self.buffer = '' + + def write(self, data): + self.buffer += data + + def flush(self): + if self.buffer: + self.sink.write(self.buffer) + self._flush() + +class SizeBufferedFilter(BufferedFilter): + + """A size-buffered filter only in fixed size chunks (excepting the + final chunk).""" + + def __init__(self, bufferSize): + BufferedFilter.__init__(self) + self.bufferSize = bufferSize + + def write(self, data): + BufferedFilter.write(self, data) + while len(self.buffer) > self.bufferSize: + chunk, self.buffer = self.buffer[:self.bufferSize], self.buffer[self.bufferSize:] + self.sink.write(chunk) + +class LineBufferedFilter(BufferedFilter): + + """A line-buffered filter only lets data through when it sees + whole lines.""" + + def __init__(self): + BufferedFilter.__init__(self) + + def write(self, data): + BufferedFilter.write(self, data) + chunks = self.buffer.split('\n') + for chunk in chunks[:-1]: + self.sink.write(chunk + '\n') + self.buffer = chunks[-1] + +class MaximallyBufferedFilter(BufferedFilter): + + """A maximally-buffered filter only lets its data through on the final + close. It ignores flushes.""" + + def __init__(self): + BufferedFilter.__init__(self) + + def flush(self): pass + + def close(self): + if self.buffer: + BufferedFilter.flush(self) + self.sink.close() + + +class Context: + + """An interpreter context, which encapsulates a name, an input + file object, and a parser object.""" + + DEFAULT_UNIT = 'lines' + + def __init__(self, name, line=0, units=DEFAULT_UNIT): + self.name = name + self.line = line + self.units = units + self.pause = False + + def bump(self, quantity=1): + if self.pause: + self.pause = False + else: + self.line += quantity + + def identify(self): + return self.name, self.line + + def __str__(self): + if self.units == self.DEFAULT_UNIT: + return "%s:%s" % (self.name, self.line) + else: + return "%s:%s[%s]" % (self.name, self.line, self.units) + + +class Hook: + + """The base class for implementing hooks.""" + + def __init__(self): + self.interpreter = None + + def register(self, interpreter): + self.interpreter = interpreter + + def deregister(self, interpreter): + if interpreter is not self.interpreter: + raise Error("hook not associated with this interpreter") + self.interpreter = None + + def push(self): + self.interpreter.push() + + def pop(self): + self.interpreter.pop() + + def null(self): pass + + def atStartup(self): pass + def atReady(self): pass + def atFinalize(self): pass + def atShutdown(self): pass + def atParse(self, scanner, locals): pass + def atToken(self, token): pass + def atHandle(self, meta): pass + def atInteract(self): pass + + def beforeInclude(self, name, file, locals): pass + def afterInclude(self): pass + + def beforeExpand(self, string, locals): pass + def afterExpand(self, result): pass + + def beforeFile(self, name, file, locals): pass + def afterFile(self): pass + + def beforeBinary(self, name, file, chunkSize, locals): pass + def afterBinary(self): pass + + def beforeString(self, name, string, locals): pass + def afterString(self): pass + + def beforeQuote(self, string): pass + def afterQuote(self, result): pass + + def beforeEscape(self, string, more): pass + def afterEscape(self, result): pass + + def beforeControl(self, type, rest, locals): pass + def afterControl(self): pass + + def beforeSignificate(self, key, value, locals): pass + def afterSignificate(self): pass + + def beforeAtomic(self, name, value, locals): pass + def afterAtomic(self): pass + + def beforeMulti(self, name, values, locals): pass + def afterMulti(self): pass + + def beforeImport(self, name, locals): pass + def afterImport(self): pass + + def beforeClause(self, catch, locals): pass + def afterClause(self, exception, variable): pass + + def beforeSerialize(self, expression, locals): pass + def afterSerialize(self): pass + + def beforeDefined(self, name, locals): pass + def afterDefined(self, result): pass + + def beforeLiteral(self, text): pass + def afterLiteral(self): pass + + def beforeEvaluate(self, expression, locals): pass + def afterEvaluate(self, result): pass + + def beforeExecute(self, statements, locals): pass + def afterExecute(self): pass + + def beforeSingle(self, source, locals): pass + def afterSingle(self): pass + +class VerboseHook(Hook): + + """A verbose hook that reports all information received by the + hook interface. This class dynamically scans the Hook base class + to ensure that all hook methods are properly represented.""" + + EXEMPT_ATTRIBUTES = ['register', 'deregister', 'push', 'pop'] + + def __init__(self, output=sys.stderr): + Hook.__init__(self) + self.output = output + self.indent = 0 + + class FakeMethod: + """This is a proxy method-like object.""" + def __init__(self, hook, name): + self.hook = hook + self.name = name + + def __call__(self, **keywords): + self.hook.output.write("%s%s: %s\n" % + (' ' * self.hook.indent, + self.name, repr(keywords))) + + for attribute in dir(Hook): + if (attribute[:1] != '_' and + attribute not in self.EXEMPT_ATTRIBUTES): + self.__dict__[attribute] = FakeMethod(self, attribute) + + +class Token: + + """An element of expansion.""" + + def run(self, interpreter, locals): + raise NotImplementedError + + def string(self): + raise NotImplementedError + + def __str__(self): return self.string() + +class NullToken(Token): + """A chunk of data not containing markups.""" + def __init__(self, data): + self.data = data + + def run(self, interpreter, locals): + interpreter.write(self.data) + + def string(self): + return self.data + +class ExpansionToken(Token): + """A token that involves an expansion.""" + def __init__(self, prefix, first): + self.prefix = prefix + self.first = first + + def scan(self, scanner): + pass + + def run(self, interpreter, locals): + pass + +class WhitespaceToken(ExpansionToken): + """A whitespace markup.""" + def string(self): + return '%s%s' % (self.prefix, self.first) + +class LiteralToken(ExpansionToken): + """A literal markup.""" + def run(self, interpreter, locals): + interpreter.write(self.first) + + def string(self): + return '%s%s' % (self.prefix, self.first) + +class PrefixToken(ExpansionToken): + """A prefix markup.""" + def run(self, interpreter, locals): + interpreter.write(interpreter.prefix) + + def string(self): + return self.prefix * 2 + +class CommentToken(ExpansionToken): + """A comment markup.""" + def scan(self, scanner): + loc = scanner.find('\n') + if loc >= 0: + self.comment = scanner.chop(loc, 1) + else: + raise TransientParseError("comment expects newline") + + def string(self): + return '%s#%s\n' % (self.prefix, self.comment) + +class ContextNameToken(ExpansionToken): + """A context name change markup.""" + def scan(self, scanner): + loc = scanner.find('\n') + if loc >= 0: + self.name = scanner.chop(loc, 1).strip() + else: + raise TransientParseError("context name expects newline") + + def run(self, interpreter, locals): + context = interpreter.context() + context.name = self.name + +class ContextLineToken(ExpansionToken): + """A context line change markup.""" + def scan(self, scanner): + loc = scanner.find('\n') + if loc >= 0: + try: + self.line = int(scanner.chop(loc, 1)) + except ValueError: + raise ParseError("context line requires integer") + else: + raise TransientParseError("context line expects newline") + + def run(self, interpreter, locals): + context = interpreter.context() + context.line = self.line + context.pause = True + +class EscapeToken(ExpansionToken): + """An escape markup.""" + def scan(self, scanner): + try: + code = scanner.chop(1) + result = None + if code in '()[]{}\'\"\\': # literals + result = code + elif code == '0': # NUL + result = '\x00' + elif code == 'a': # BEL + result = '\x07' + elif code == 'b': # BS + result = '\x08' + elif code == 'd': # decimal code + decimalCode = scanner.chop(3) + result = chr(int(decimalCode, 10)) + elif code == 'e': # ESC + result = '\x1b' + elif code == 'f': # FF + result = '\x0c' + elif code == 'h': # DEL + result = '\x7f' + elif code == 'n': # LF (newline) + result = '\x0a' + elif code == 'N': # Unicode character name + theSubsystem.assertUnicode() + import unicodedata + if scanner.chop(1) != '{': + raise ParseError("Unicode name escape should be \\N{...}") + i = scanner.find('}') + name = scanner.chop(i, 1) + try: + result = unicodedata.lookup(name) + except KeyError: + raise SubsystemError("unknown Unicode character name: %s" % name) + elif code == 'o': # octal code + octalCode = scanner.chop(3) + result = chr(int(octalCode, 8)) + elif code == 'q': # quaternary code + quaternaryCode = scanner.chop(4) + result = chr(int(quaternaryCode, 4)) + elif code == 'r': # CR + result = '\x0d' + elif code in 's ': # SP + result = ' ' + elif code == 't': # HT + result = '\x09' + elif code in 'u': # Unicode 16-bit hex literal + theSubsystem.assertUnicode() + hexCode = scanner.chop(4) + result = _unichr(int(hexCode, 16)) + elif code in 'U': # Unicode 32-bit hex literal + theSubsystem.assertUnicode() + hexCode = scanner.chop(8) + result = _unichr(int(hexCode, 16)) + elif code == 'v': # VT + result = '\x0b' + elif code == 'x': # hexadecimal code + hexCode = scanner.chop(2) + result = chr(int(hexCode, 16)) + elif code == 'z': # EOT + result = '\x04' + elif code == '^': # control character + controlCode = scanner.chop(1).upper() + if controlCode >= '@' and controlCode <= '`': + result = chr(ord(controlCode) - ord('@')) + elif controlCode == '?': + result = '\x7f' + else: + raise ParseError("invalid escape control code") + else: + raise ParseError("unrecognized escape code") + assert result is not None + self.code = result + except ValueError: + raise ParseError("invalid numeric escape code") + + def run(self, interpreter, locals): + interpreter.write(self.code) + + def string(self): + return '%s\\x%02x' % (self.prefix, ord(self.code)) + +class SignificatorToken(ExpansionToken): + """A significator markup.""" + def scan(self, scanner): + loc = scanner.find('\n') + if loc >= 0: + line = scanner.chop(loc, 1) + if not line: + raise ParseError("significator must have nonblank key") + if line[0] in ' \t\v\n': + raise ParseError("no whitespace between % and key") + # Work around a subtle CPython-Jython difference by stripping + # the string before splitting it: 'a '.split(None, 1) has two + # elements in Jython 2.1). + fields = line.strip().split(None, 1) + if len(fields) == 2 and fields[1] == '': + fields.pop() + self.key = fields[0] + if len(fields) < 2: + fields.append(None) + self.key, self.valueCode = fields + else: + raise TransientParseError("significator expects newline") + + def run(self, interpreter, locals): + value = self.valueCode + if value is not None: + value = interpreter.evaluate(value.strip(), locals) + interpreter.significate(self.key, value) + + def string(self): + if self.valueCode is None: + return '%s%%%s\n' % (self.prefix, self.key) + else: + return '%s%%%s %s\n' % (self.prefix, self.key, self.valueCode) + +class ExpressionToken(ExpansionToken): + """An expression markup.""" + def scan(self, scanner): + z = scanner.complex('(', ')', 0) + try: + q = scanner.next('$', 0, z, True) + except ParseError: + q = z + try: + i = scanner.next('?', 0, q, True) + try: + j = scanner.next('!', i, q, True) + except ParseError: + try: + j = scanner.next(':', i, q, True) # DEPRECATED + except ParseError: + j = q + except ParseError: + i = j = q + code = scanner.chop(z, 1) + self.testCode = code[:i] + self.thenCode = code[i + 1:j] + self.elseCode = code[j + 1:q] + self.exceptCode = code[q + 1:z] + + def run(self, interpreter, locals): + try: + result = interpreter.evaluate(self.testCode, locals) + if self.thenCode: + if result: + result = interpreter.evaluate(self.thenCode, locals) + else: + if self.elseCode: + result = interpreter.evaluate(self.elseCode, locals) + else: + result = None + except SyntaxError: + # Don't catch syntax errors; let them through. + raise + except: + if self.exceptCode: + result = interpreter.evaluate(self.exceptCode, locals) + else: + raise + if result is not None: + interpreter.write(str(result)) + + def string(self): + result = self.testCode + if self.thenCode: + result += '?' + self.thenCode + if self.elseCode: + result += '!' + self.elseCode + if self.exceptCode: + result += '$' + self.exceptCode + return '%s(%s)' % (self.prefix, result) + +class StringLiteralToken(ExpansionToken): + """A string token markup.""" + def scan(self, scanner): + scanner.retreat() + assert scanner[0] == self.first + i = scanner.quote() + self.literal = scanner.chop(i) + + def run(self, interpreter, locals): + interpreter.literal(self.literal) + + def string(self): + return '%s%s' % (self.prefix, self.literal) + +class SimpleExpressionToken(ExpansionToken): + """A simple expression markup.""" + def scan(self, scanner): + i = scanner.simple() + self.code = self.first + scanner.chop(i) + + def run(self, interpreter, locals): + interpreter.serialize(self.code, locals) + + def string(self): + return '%s%s' % (self.prefix, self.code) + +class ReprToken(ExpansionToken): + """A repr markup.""" + def scan(self, scanner): + i = scanner.next('`', 0) + self.code = scanner.chop(i, 1) + + def run(self, interpreter, locals): + interpreter.write(repr(interpreter.evaluate(self.code, locals))) + + def string(self): + return '%s`%s`' % (self.prefix, self.code) + +class InPlaceToken(ExpansionToken): + """An in-place markup.""" + def scan(self, scanner): + i = scanner.next(':', 0) + j = scanner.next(':', i + 1) + self.code = scanner.chop(i, j - i + 1) + + def run(self, interpreter, locals): + interpreter.write("%s:%s:" % (interpreter.prefix, self.code)) + try: + interpreter.serialize(self.code, locals) + finally: + interpreter.write(":") + + def string(self): + return '%s:%s::' % (self.prefix, self.code) + +class StatementToken(ExpansionToken): + """A statement markup.""" + def scan(self, scanner): + i = scanner.complex('{', '}', 0) + self.code = scanner.chop(i, 1) + + def run(self, interpreter, locals): + interpreter.execute(self.code, locals) + + def string(self): + return '%s{%s}' % (self.prefix, self.code) + +class CustomToken(ExpansionToken): + """A custom markup.""" + def scan(self, scanner): + i = scanner.complex('<', '>', 0) + self.contents = scanner.chop(i, 1) + + def run(self, interpreter, locals): + interpreter.invokeCallback(self.contents) + + def string(self): + return '%s<%s>' % (self.prefix, self.contents) + +class ControlToken(ExpansionToken): + + """A control token.""" + + PRIMARY_TYPES = ['if', 'for', 'while', 'try', 'def'] + SECONDARY_TYPES = ['elif', 'else', 'except', 'finally'] + TERTIARY_TYPES = ['continue', 'break'] + GREEDY_TYPES = ['if', 'elif', 'for', 'while', 'def', 'end'] + END_TYPES = ['end'] + + IN_RE = re.compile(r"\bin\b") + + def scan(self, scanner): + scanner.acquire() + i = scanner.complex('[', ']', 0) + self.contents = scanner.chop(i, 1) + fields = self.contents.strip().split(' ', 1) + if len(fields) > 1: + self.type, self.rest = fields + else: + self.type = fields[0] + self.rest = None + self.subtokens = [] + if self.type in self.GREEDY_TYPES and self.rest is None: + raise ParseError("control '%s' needs arguments" % self.type) + if self.type in self.PRIMARY_TYPES: + self.subscan(scanner, self.type) + self.kind = 'primary' + elif self.type in self.SECONDARY_TYPES: + self.kind = 'secondary' + elif self.type in self.TERTIARY_TYPES: + self.kind = 'tertiary' + elif self.type in self.END_TYPES: + self.kind = 'end' + else: + raise ParseError("unknown control markup: '%s'" % self.type) + scanner.release() + + def subscan(self, scanner, primary): + """Do a subscan for contained tokens.""" + while True: + token = scanner.one() + if token is None: + raise TransientParseError("control '%s' needs more tokens" % primary) + if (isinstance(token, ControlToken) and + token.type in self.END_TYPES): + if token.rest != primary: + raise ParseError("control must end with 'end %s'" % primary) + break + self.subtokens.append(token) + + def build(self, allowed=None): + """Process the list of subtokens and divide it into a list of + 2-tuples, consisting of the dividing tokens and the list of + subtokens that follow them. If allowed is specified, it will + represent the list of the only secondary markup types which + are allowed.""" + if allowed is None: + allowed = SECONDARY_TYPES + result = [] + latest = [] + result.append((self, latest)) + for subtoken in self.subtokens: + if (isinstance(subtoken, ControlToken) and + subtoken.kind == 'secondary'): + if subtoken.type not in allowed: + raise ParseError("control unexpected secondary: '%s'" % subtoken.type) + latest = [] + result.append((subtoken, latest)) + else: + latest.append(subtoken) + return result + + def run(self, interpreter, locals): + interpreter.invoke('beforeControl', type=self.type, rest=self.rest, + locals=locals) + if self.type == 'if': + info = self.build(['elif', 'else']) + elseTokens = None + if info[-1][0].type == 'else': + elseTokens = info.pop()[1] + for secondary, subtokens in info: + if secondary.type not in ('if', 'elif'): + raise ParseError("control 'if' unexpected secondary: '%s'" % secondary.type) + if interpreter.evaluate(secondary.rest, locals): + self.subrun(subtokens, interpreter, locals) + break + else: + if elseTokens: + self.subrun(elseTokens, interpreter, locals) + elif self.type == 'for': + sides = self.IN_RE.split(self.rest, 1) + if len(sides) != 2: + raise ParseError("control expected 'for x in seq'") + iterator, sequenceCode = sides + info = self.build(['else']) + elseTokens = None + if info[-1][0].type == 'else': + elseTokens = info.pop()[1] + if len(info) != 1: + raise ParseError("control 'for' expects at most one 'else'") + sequence = interpreter.evaluate(sequenceCode, locals) + for element in sequence: + try: + interpreter.assign(iterator, element, locals) + self.subrun(info[0][1], interpreter, locals) + except ContinueFlow: + continue + except BreakFlow: + break + else: + if elseTokens: + self.subrun(elseTokens, interpreter, locals) + elif self.type == 'while': + testCode = self.rest + info = self.build(['else']) + elseTokens = None + if info[-1][0].type == 'else': + elseTokens = info.pop()[1] + if len(info) != 1: + raise ParseError("control 'while' expects at most one 'else'") + atLeastOnce = False + while True: + try: + if not interpreter.evaluate(testCode, locals): + break + atLeastOnce = True + self.subrun(info[0][1], interpreter, locals) + except ContinueFlow: + continue + except BreakFlow: + break + if not atLeastOnce and elseTokens: + self.subrun(elseTokens, interpreter, locals) + elif self.type == 'try': + info = self.build(['except', 'finally']) + if len(info) == 1: + raise ParseError("control 'try' needs 'except' or 'finally'") + type = info[-1][0].type + if type == 'except': + for secondary, _tokens in info[1:]: + if secondary.type != 'except': + raise ParseError("control 'try' cannot have 'except' and 'finally'") + else: + assert type == 'finally' + if len(info) != 2: + raise ParseError("control 'try' can only have one 'finally'") + if type == 'except': + try: + self.subrun(info[0][1], interpreter, locals) + except FlowError: + raise + except Exception: + e = sys.exc_info()[1] + for secondary, tokens in info[1:]: + exception, variable = interpreter.clause(secondary.rest) + if variable is not None: + interpreter.assign(variable, e) + if isinstance(e, exception): + self.subrun(tokens, interpreter, locals) + break + else: + raise + else: + try: + self.subrun(info[0][1], interpreter, locals) + finally: + self.subrun(info[1][1], interpreter, locals) + elif self.type == 'continue': + raise ContinueFlow("control 'continue' without 'for', 'while'") + elif self.type == 'break': + raise BreakFlow("control 'break' without 'for', 'while'") + elif self.type == 'def': + signature = self.rest + definition = self.substring() + code = ('def %s:\n' + ' r"""%s"""\n' + ' return %s.expand(r"""%s""", locals())\n' % + (signature, definition, interpreter.pseudo, definition)) + interpreter.execute(code, locals) + elif self.type == 'end': + raise ParseError("control 'end' requires primary markup") + else: + raise ParseError("control '%s' cannot be at this level" % self.type) + interpreter.invoke('afterControl') + + def subrun(self, tokens, interpreter, locals): + """Execute a sequence of tokens.""" + for token in tokens: + token.run(interpreter, locals) + + def substring(self): + return ''.join(str(x) for x in self.subtokens) + + def string(self): + if self.kind == 'primary': + return ('%s[%s]%s%s[end %s]' % + (self.prefix, self.contents, self.substring(), + self.prefix, self.type)) + else: + return '%s[%s]' % (self.prefix, self.contents) + + +class Scanner: + + """A scanner holds a buffer for lookahead parsing and has the + ability to scan for special symbols and indicators in that + buffer.""" + + # This is the token mapping table that maps first characters to + # token classes. + TOKEN_MAP = [ + (None, PrefixToken), + (' \t\v\r\n', WhitespaceToken), + (')]}', LiteralToken), + ('\\', EscapeToken), + ('#', CommentToken), + ('?', ContextNameToken), + ('!', ContextLineToken), + ('%', SignificatorToken), + ('(', ExpressionToken), + (IDENTIFIER_FIRST_CHARS, SimpleExpressionToken), + ('\'\"', StringLiteralToken), + ('`', ReprToken), + (':', InPlaceToken), + ('[', ControlToken), + ('{', StatementToken), + ('<', CustomToken), + ] + + def __init__(self, prefix, data=''): + self.prefix = prefix + self.pointer = 0 + self.buffer = data + self.lock = 0 + + def __nonzero__(self): return self.pointer < len(self.buffer) # 2.x + def __bool__(self): return self.pointer < len(self.buffer) # 3.x + def __len__(self): return len(self.buffer) - self.pointer + + def __getitem__(self, index): + if isinstance(index, slice): + assert index.step is None or index.step == 1 + return self.__getslice__(index.start, index.stop) + else: + return self.buffer[self.pointer + index] + + def __getslice__(self, start, stop): + if start is None: + start = 0 + if stop is None: + stop = len(self) + if stop > len(self): + stop = len(self) + return self.buffer[self.pointer + start:self.pointer + stop] + + def advance(self, count=1): + """Advance the pointer count characters.""" + self.pointer += count + + def retreat(self, count=1): + self.pointer = self.pointer - count + if self.pointer < 0: + raise ParseError("can't retreat back over synced out chars") + + def set(self, data): + """Start the scanner digesting a new batch of data; start the pointer + over from scratch.""" + self.pointer = 0 + self.buffer = data + + def feed(self, data): + """Feed some more data to the scanner.""" + self.buffer += data + + def chop(self, count=None, slop=0): + """Chop the first count + slop characters off the front, and return + the first count. If count is not specified, then return + everything.""" + if count is None: + assert slop == 0 + count = len(self) + if count > len(self): + raise TransientParseError("not enough data to read") + result = self[:count] + self.advance(count + slop) + return result + + def acquire(self): + """Lock the scanner so it doesn't destroy data on sync.""" + self.lock += 1 + + def release(self): + """Unlock the scanner.""" + self.lock -= 1 + + def sync(self): + """Sync up the buffer with the read head.""" + if self.lock == 0 and self.pointer != 0: + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + + def unsync(self): + """Undo changes; reset the read head.""" + if self.pointer != 0: + self.lock = 0 + self.pointer = 0 + + def rest(self): + """Get the remainder of the buffer.""" + return self[:] + + def read(self, i=0, count=1): + """Read count chars starting from i; raise a transient error if + there aren't enough characters remaining.""" + if len(self) < i + count: + raise TransientParseError("need more data to read") + else: + return self[i:i + count] + + def check(self, i, archetype=None): + """Scan for the next single or triple quote, with the specified + archetype. Return the found quote or None.""" + quote = None + if self[i] in '\'\"': + quote = self[i] + if len(self) - i < 3: + for j in range(i, len(self)): + if self[i] == quote: + return quote + else: + raise TransientParseError("need to scan for rest of quote") + if self[i + 1] == self[i + 2] == quote: + quote = quote * 3 + if quote is not None: + if archetype is None: + return quote + else: + if archetype == quote: + return quote + elif len(archetype) < len(quote) and archetype[0] == quote[0]: + return archetype + else: + return None + else: + return None + + def find(self, sub, start=0, end=None): + """Find the next occurrence of the character, or return -1.""" + if end is not None: + return self.rest().find(sub, start, end) + else: + return self.rest().find(sub, start) + + def last(self, char, start=0, end=None): + """Find the first character that is _not_ the specified character.""" + if end is None: + end = len(self) + i = start + while i < end: + if self[i] != char: + return i + i += 1 + else: + raise TransientParseError("expecting other than %s" % char) + + def next(self, target, start=0, end=None, mandatory=False): + """Scan for the next occurrence of one of the characters in + the target string; optionally, make the scan mandatory.""" + if mandatory: + assert end is not None + quote = None + if end is None: + end = len(self) + i = start + while i < end: + newQuote = self.check(i, quote) + if newQuote: + if newQuote == quote: + quote = None + else: + quote = newQuote + i += len(newQuote) + else: + c = self[i] + if quote: + if c == '\\': + i += 1 + else: + if c in target: + return i + i += 1 + else: + if mandatory: + raise ParseError("expecting %s, not found" % target) + else: + raise TransientParseError("expecting ending character") + + def quote(self, start=0, end=None, mandatory=False): + """Scan for the end of the next quote.""" + assert self[start] in '\'\"' + quote = self.check(start) + if end is None: + end = len(self) + i = start + len(quote) + while i < end: + newQuote = self.check(i, quote) + if newQuote: + i += len(newQuote) + if newQuote == quote: + return i + else: + c = self[i] + if c == '\\': + i += 1 + i += 1 + else: + if mandatory: + raise ParseError("expecting end of string literal") + else: + raise TransientParseError("expecting end of string literal") + + def nested(self, enter, exit, start=0, end=None): + """Scan from i for an ending sequence, respecting entries and exits + only.""" + depth = 0 + if end is None: + end = len(self) + i = start + while i < end: + c = self[i] + if c == enter: + depth += 1 + elif c == exit: + depth -= 1 + if depth < 0: + return i + i += 1 + else: + raise TransientParseError("expecting end of complex expression") + + def complex(self, enter, exit, start=0, end=None, skip=None): + """Scan from i for an ending sequence, respecting quotes, + entries and exits.""" + quote = None + depth = 0 + if end is None: + end = len(self) + last = None + i = start + while i < end: + newQuote = self.check(i, quote) + if newQuote: + if newQuote == quote: + quote = None + else: + quote = newQuote + i += len(newQuote) + else: + c = self[i] + if quote: + if c == '\\': + i += 1 + else: + if skip is None or last != skip: + if c == enter: + depth += 1 + elif c == exit: + depth -= 1 + if depth < 0: + return i + last = c + i += 1 + else: + raise TransientParseError("expecting end of complex expression") + + def word(self, start=0): + """Scan from i for a simple word.""" + length = len(self) + i = start + while i < length: + if not self[i] in IDENTIFIER_CHARS: + return i + i += 1 + else: + raise TransientParseError("expecting end of word") + + def phrase(self, start=0): + """Scan from i for a phrase (e.g., 'word', 'f(a, b, c)', 'a[i]', or + combinations like 'x[i](a)'.""" + # Find the word. + i = self.word(start) + while i < len(self) and self[i] in '([{': + enter = self[i] + if enter == '{': + raise ParseError("curly braces can't open simple expressions") + exit = ENDING_CHARS[enter] + i = self.complex(enter, exit, i + 1) + 1 + return i + + def simple(self, start=0): + """Scan from i for a simple expression, which consists of one + more phrases separated by dots.""" + i = self.phrase(start) + length = len(self) + while i < length and self[i] == '.': + i = self.phrase(i) + # Make sure we don't end with a trailing dot. + while i > 0 and self[i - 1] == '.': + i -= 1 + return i + + def one(self): + """Parse and return one token, or None if the scanner is empty.""" + if not self: + return None + if not self.prefix: + loc = -1 + else: + loc = self.find(self.prefix) + if loc < 0: + # If there's no prefix in the buffer, then set the location to + # the end so the whole thing gets processed. + loc = len(self) + if loc == 0: + # If there's a prefix at the beginning of the buffer, process + # an expansion. + prefix = self.chop(1) + assert prefix == self.prefix + first = self.chop(1) + if first == self.prefix: + first = None + for firsts, factory in self.TOKEN_MAP: + if firsts is None: + if first is None: + break + elif first in firsts: + break + else: + raise ParseError("unknown markup: %s%s" % (self.prefix, first)) + token = factory(self.prefix, first) + try: + token.scan(self) + except TransientParseError: + # If a transient parse error occurs, reset the buffer pointer + # so we can (conceivably) try again later. + self.unsync() + raise + else: + # Process everything up to loc as a null token. + data = self.chop(loc) + token = NullToken(data) + self.sync() + return token + + +class Interpreter: + + """An interpreter can process chunks of EmPy code.""" + + # Constants. + + VERSION = __version__ + SIGNIFICATOR_RE_SUFFIX = SIGNIFICATOR_RE_SUFFIX + SIGNIFICATOR_RE_STRING = None + + # Types. + + Interpreter = None # define this below to prevent a circular reference + Hook = Hook # DEPRECATED + Filter = Filter # DEPRECATED + NullFilter = NullFilter # DEPRECATED + FunctionFilter = FunctionFilter # DEPRECATED + StringFilter = StringFilter # DEPRECATED + BufferedFilter = BufferedFilter # DEPRECATED + SizeBufferedFilter = SizeBufferedFilter # DEPRECATED + LineBufferedFilter = LineBufferedFilter # DEPRECATED + MaximallyBufferedFilter = MaximallyBufferedFilter # DEPRECATED + + # Tables. + + ESCAPE_CODES = {0x00: '0', 0x07: 'a', 0x08: 'b', 0x1b: 'e', 0x0c: 'f', + 0x7f: 'h', 0x0a: 'n', 0x0d: 'r', 0x09: 't', 0x0b: 'v', + 0x04: 'z'} + + ASSIGN_TOKEN_RE = re.compile(r"[_a-zA-Z][_a-zA-Z0-9]*|\(|\)|,") + + DEFAULT_OPTIONS = {BANGPATH_OPT: True, + BUFFERED_OPT: False, + RAW_OPT: False, + EXIT_OPT: True, + FLATTEN_OPT: False, + OVERRIDE_OPT: True, + CALLBACK_OPT: False} + + _wasProxyInstalled = False # was a proxy installed? + + # Construction, initialization, destruction. + + def __init__(self, output=None, argv=None, prefix=DEFAULT_PREFIX, + pseudo=None, options=None, globals=None, hooks=None): + self.interpreter = self # DEPRECATED + # Set up the stream. + if output is None: + output = UncloseableFile(sys.__stdout__) + self.output = output + self.prefix = prefix + if pseudo is None: + pseudo = DEFAULT_PSEUDOMODULE_NAME + self.pseudo = pseudo + if argv is None: + argv = [DEFAULT_SCRIPT_NAME] + self.argv = argv + self.args = argv[1:] + if options is None: + options = {} + self.options = options + # Initialize any hooks. + self.hooksEnabled = None # special sentinel meaning "false until added" + self.hooks = [] + if hooks is None: + hooks = [] + for hook in hooks: + self.register(hook) + # Initialize callback. + self.callback = None + # Finalizers. + self.finals = [] + # The interpreter stacks. + self.contexts = Stack() + self.streams = Stack() + # Now set up the globals. + self.globals = globals + self.fix() + self.history = Stack() + # Install a proxy stdout if one hasn't been already. + self.installProxy() + # Finally, reset the state of all the stacks. + self.reset() + # Okay, now flatten the namespaces if that option has been set. + if self.options.get(FLATTEN_OPT, False): + self.flatten() + # Set up old pseudomodule attributes. + if prefix is None: + self.SIGNIFICATOR_RE_STRING = None + else: + self.SIGNIFICATOR_RE_STRING = prefix + self.SIGNIFICATOR_RE_SUFFIX + self.Interpreter = self.__class__ + # Done. Now declare that we've started up. + self.invoke('atStartup') + + def __del__(self): + self.shutdown() + + def __repr__(self): + return ('<%s pseudomodule/interpreter at 0x%x>' % + (self.pseudo, id(self))) + + def ready(self): + """Declare the interpreter ready for normal operations.""" + self.invoke('atReady') + + def fix(self): + """Reset the globals, stamping in the pseudomodule.""" + if self.globals is None: + self.globals = {} + # Make sure that there is no collision between two interpreters' + # globals. + if self.pseudo in self.globals: + if self.globals[self.pseudo] is not self: + raise Error("interpreter globals collision") + self.globals[self.pseudo] = self + + def unfix(self): + """Remove the pseudomodule (if present) from the globals.""" + UNWANTED_KEYS = [self.pseudo, '__builtins__'] + for unwantedKey in UNWANTED_KEYS: + if unwantedKey in self.globals: + del self.globals[unwantedKey] + + def update(self, other): + """Update the current globals dictionary with another dictionary.""" + self.globals.update(other) + self.fix() + + def clear(self): + """Clear out the globals dictionary with a brand new one.""" + self.globals = {} + self.fix() + + def save(self, deep=True): + if deep: + copyMethod = copy.deepcopy + else: + copyMethod = copy.copy + """Save a copy of the current globals on the history stack.""" + self.unfix() + self.history.push(copyMethod(self.globals)) + self.fix() + + def restore(self, destructive=True): + """Restore the topmost historic globals.""" + if destructive: + fetchMethod = self.history.pop + else: + fetchMethod = self.history.top + self.unfix() + self.globals = fetchMethod() + self.fix() + + def shutdown(self): + """Declare this interpreting session over; close the stream file + object. This method is idempotent.""" + if self.streams is not None: + try: + self.finalize() + self.invoke('atShutdown') + while self.streams: + stream = self.streams.pop() + stream.close() + finally: + self.streams = None + + def ok(self): + """Is the interpreter still active?""" + return self.streams is not None + + # Writeable file-like methods. + + def write(self, data): + self.stream().write(data) + + def writelines(self, stuff): + self.stream().writelines(stuff) + + def flush(self): + self.stream().flush() + + def close(self): + self.shutdown() + + # Stack-related activity. + + def context(self): + return self.contexts.top() + + def stream(self): + return self.streams.top() + + def reset(self): + self.contexts.purge() + self.streams.purge() + self.streams.push(Stream(self.output)) + if self.options.get(OVERRIDE_OPT, True): + sys.stdout.clear(self) + + def push(self): + if self.options.get(OVERRIDE_OPT, True): + sys.stdout.push(self) + + def pop(self): + if self.options.get(OVERRIDE_OPT, True): + sys.stdout.pop(self) + + # Higher-level operations. + + def include(self, fileOrFilename, locals=None): + """Do an include pass on a file or filename.""" + if isinstance(fileOrFilename, _str): + # Either it's a string representing a filename ... + filename = fileOrFilename + name = filename + file = theSubsystem.open(filename, 'r') + else: + # ... or a file object. + file = fileOrFilename + name = "<%s>" % str(file.__class__) + self.invoke('beforeInclude', name=name, file=file, locals=locals) + self.file(file, name, locals) + self.invoke('afterInclude') + + def expand(self, data, locals=None): + """Do an explicit expansion on a subordinate stream.""" + outFile = StringIO() + stream = Stream(outFile) + self.invoke('beforeExpand', string=data, locals=locals) + self.streams.push(stream) + try: + self.string(data, '', locals) + stream.flush() + expansion = outFile.getvalue() + self.invoke('afterExpand', result=expansion) + return expansion + finally: + self.streams.pop() + + def quote(self, data): + """Quote the given string so that if it were expanded it would + evaluate to the original.""" + self.invoke('beforeQuote', string=data) + scanner = Scanner(self.prefix, data) + result = [] + i = 0 + try: + j = scanner.next(self.prefix, i) + result.append(data[i:j]) + result.append(self.prefix * 2) + i = j + 1 + except TransientParseError: + pass + result.append(data[i:]) + result = ''.join(result) + self.invoke('afterQuote', result=result) + return result + + def escape(self, data, more=''): + """Escape a string so that nonprintable characters are replaced + with compatible EmPy expansions.""" + self.invoke('beforeEscape', string=data, more=more) + result = [] + for char in data: + if char < ' ' or char > '~': + charOrd = ord(char) + if charOrd in Interpreter.ESCAPE_CODES: + result.append(self.prefix + '\\' + + Interpreter.ESCAPE_CODES[charOrd]) + else: + result.append(self.prefix + '\\x%02x' % charOrd) + elif char in more: + result.append(self.prefix + '\\' + char) + else: + result.append(char) + result = ''.join(result) + self.invoke('afterEscape', result=result) + return result + + # Processing. + + def wrap(self, callable, args): + """Wrap around an application of a callable and handle errors. + Return whether no error occurred.""" + try: + callable(*args) + self.reset() + return True + except KeyboardInterrupt: + # Handle keyboard interrupts specially: we should always exit + # from these. + e = sys.exc_info()[1] + self.fail(e, True) + except Exception: + # A standard exception (other than a keyboard interrupt). + e = sys.exc_info()[1] + self.fail(e) + except: + # If we get here, then either it's an exception not derived from + # Exception or it's a string exception, so get the error type + # from the sys module. + e = sys.exc_info()[1] + self.fail(e) + # An error occurred if we leak through to here, so do cleanup. + self.reset() + return False + + def interact(self): + """Perform interaction.""" + self.invoke('atInteract') + done = False + while not done: + result = self.wrap(self.file, (sys.stdin, '')) + if self.options.get(EXIT_OPT, True): + done = True + else: + if result: + done = True + else: + self.reset() + + def fail(self, error, fatal=False): + """Handle an actual error that occurred.""" + if self.options.get(BUFFERED_OPT, False): + try: + self.output.abort() + except AttributeError: + # If the output file object doesn't have an abort method, + # something got mismatched, but it's too late to do + # anything about it now anyway, so just ignore it. + pass + meta = self.meta(error) + self.handle(meta) + if self.options.get(RAW_OPT, False): + raise + if fatal or self.options.get(EXIT_OPT, True): + sys.exit(FAILURE_CODE) + + def file(self, file, name='', locals=None): + """Parse the entire contents of a file-like object, line by line.""" + context = Context(name) + self.contexts.push(context) + self.invoke('beforeFile', name=name, file=file, locals=locals) + scanner = Scanner(self.prefix) + first = True + done = False + while not done: + self.context().bump() + line = file.readline() + if first: + if self.options.get(BANGPATH_OPT, True) and self.prefix: + # Replace a bangpath at the beginning of the first line + # with an EmPy comment. + if line.startswith(BANGPATH): + line = self.prefix + '#' + line[2:] + first = False + if line: + scanner.feed(line) + else: + done = True + self.safe(scanner, done, locals) + self.invoke('afterFile') + self.contexts.pop() + + def binary(self, file, name='', chunkSize=0, locals=None): + """Parse the entire contents of a file-like object, in chunks.""" + if chunkSize <= 0: + chunkSize = DEFAULT_CHUNK_SIZE + context = Context(name, units='bytes') + self.contexts.push(context) + self.invoke('beforeBinary', name=name, file=file, + chunkSize=chunkSize, locals=locals) + scanner = Scanner(self.prefix) + done = False + while not done: + chunk = file.read(chunkSize) + if chunk: + scanner.feed(chunk) + else: + done = True + self.safe(scanner, done, locals) + self.context().bump(len(chunk)) + self.invoke('afterBinary') + self.contexts.pop() + + def string(self, data, name='', locals=None): + """Parse a string.""" + context = Context(name) + self.contexts.push(context) + self.invoke('beforeString', name=name, string=data, locals=locals) + context.bump() + scanner = Scanner(self.prefix, data) + self.safe(scanner, True, locals) + self.invoke('afterString') + self.contexts.pop() + + def safe(self, scanner, final=False, locals=None): + """Do a protected parse. Catch transient parse errors; if + final is true, then make a final pass with a terminator, + otherwise ignore the transient parse error (more data is + pending).""" + try: + self.parse(scanner, locals) + except TransientParseError: + if final: + # If the buffer doesn't end with a newline, try tacking on + # a dummy terminator. + buffer = scanner.rest() + if buffer and buffer[-1] != '\n': + scanner.feed(self.prefix + '\n') + # A TransientParseError thrown from here is a real parse + # error. + self.parse(scanner, locals) + + def parse(self, scanner, locals=None): + """Parse and run as much from this scanner as possible.""" + self.invoke('atParse', scanner=scanner, locals=locals) + while True: + token = scanner.one() + if token is None: + break + self.invoke('atToken', token=token) + token.run(self, locals) + + # Medium-level evaluation and execution. + + def tokenize(self, name): + """Take an lvalue string and return a name or a (possibly recursive) + list of names.""" + result = [] + stack = [result] + for garbage in self.ASSIGN_TOKEN_RE.split(name): + garbage = garbage.strip() + if garbage: + raise ParseError("unexpected assignment token: '%s'" % garbage) + tokens = self.ASSIGN_TOKEN_RE.findall(name) + # While processing, put a None token at the start of any list in which + # commas actually appear. + for token in tokens: + if token == '(': + stack.append([]) + elif token == ')': + top = stack.pop() + if len(top) == 1: + top = top[0] # no None token means that it's not a 1-tuple + elif top[0] is None: + del top[0] # remove the None token for real tuples + stack[-1].append(top) + elif token == ',': + if len(stack[-1]) == 1: + stack[-1].insert(0, None) + else: + stack[-1].append(token) + # If it's a 1-tuple at the top level, turn it into a real subsequence. + if result and result[0] is None: + result = [result[1:]] + if len(result) == 1: + return result[0] + else: + return result + + def significate(self, key, value=None, locals=None): + """Declare a significator.""" + self.invoke('beforeSignificate', key=key, value=value, locals=locals) + name = '__%s__' % key + self.atomic(name, value, locals) + self.invoke('afterSignificate') + + def atomic(self, name, value, locals=None): + """Do an atomic assignment.""" + self.invoke('beforeAtomic', name=name, value=value, locals=locals) + if locals is None: + self.globals[name] = value + else: + locals[name] = value + self.invoke('afterAtomic') + + def multi(self, names, values, locals=None): + """Do a (potentially recursive) assignment.""" + self.invoke('beforeMulti', names=names, values=values, locals=locals) + # No zip in 1.5, so we have to do it manually. + i = 0 + try: + values = tuple(values) + except TypeError: + raise TypeError("unpack non-sequence") + if len(names) != len(values): + raise ValueError("unpack tuple of wrong size") + for i in range(len(names)): + name = names[i] + if isinstance(name, _str) or isinstance(name, _unicode): + self.atomic(name, values[i], locals) + else: + self.multi(name, values[i], locals) + self.invoke('afterMulti') + + def assign(self, name, value, locals=None): + """Do a potentially complex (including tuple unpacking) assignment.""" + left = self.tokenize(name) + # The return value of tokenize can either be a string or a list of + # (lists of) strings. + if isinstance(left, _str) or isinstance(left, _unicode): + self.atomic(left, value, locals) + else: + self.multi(left, value, locals) + + def import_(self, name, locals=None): + """Do an import.""" + self.invoke('beforeImport', name=name, locals=locals) + self.execute('import %s' % name, locals) + self.invoke('afterImport') + + def clause(self, catch, locals=None): + """Given the string representation of an except clause, turn it into + a 2-tuple consisting of the class name, and either a variable name + or None.""" + self.invoke('beforeClause', catch=catch, locals=locals) + if catch is None: + exceptionCode, variable = None, None + elif catch.find(',') >= 0: + exceptionCode, variable = catch.strip().split(',', 1) + variable = variable.strip() + else: + exceptionCode, variable = catch.strip(), None + if not exceptionCode: + exception = Exception + else: + exception = self.evaluate(exceptionCode, locals) + self.invoke('afterClause', exception=exception, variable=variable) + return exception, variable + + def serialize(self, expression, locals=None): + """Do an expansion, involving evaluating an expression, then + converting it to a string and writing that string to the + output if the evaluation is not None.""" + self.invoke('beforeSerialize', expression=expression, locals=locals) + result = self.evaluate(expression, locals) + if result is not None: + self.write(str(result)) + self.invoke('afterSerialize') + + def defined(self, name, locals=None): + """Return a Boolean indicating whether or not the name is + defined either in the locals or the globals.""" + self.invoke('beforeDefined', name=name, local=local) + if locals is not None: + if name in locals: + result = True + else: + result = False + elif name in self.globals: + result = True + else: + result = False + self.invoke('afterDefined', result=result) + + def literal(self, text): + """Process a string literal.""" + self.invoke('beforeLiteral', text=text) + self.serialize(text) + self.invoke('afterLiteral') + + # Low-level evaluation and execution. + + def evaluate(self, expression, locals=None): + """Evaluate an expression.""" + if expression in ('1', 'True'): return True + if expression in ('0', 'False'): return False + self.push() + try: + self.invoke('beforeEvaluate', + expression=expression, locals=locals) + if locals is not None: + result = eval(expression, self.globals, locals) + else: + result = eval(expression, self.globals) + self.invoke('afterEvaluate', result=result) + return result + finally: + self.pop() + + def execute(self, statements, locals=None): + """Execute a statement.""" + # If there are any carriage returns (as opposed to linefeeds/newlines) + # in the statements code, then remove them. Even on DOS/Windows + # platforms, + if statements.find('\r') >= 0: + statements = statements.replace('\r', '') + # If there are no newlines in the statements code, then strip any + # leading or trailing whitespace. + if statements.find('\n') < 0: + statements = statements.strip() + self.push() + try: + self.invoke('beforeExecute', + statements=statements, locals=locals) + _exec(statements, self.globals, locals) + self.invoke('afterExecute') + finally: + self.pop() + + def single(self, source, locals=None): + """Execute an expression or statement, just as if it were + entered into the Python interactive interpreter.""" + self.push() + try: + self.invoke('beforeSingle', + source=source, locals=locals) + code = compile(source, '', 'single') + _exec(code, self.globals, locals) + self.invoke('afterSingle') + finally: + self.pop() + + # Hooks. + + def register(self, hook, prepend=False): + """Register the provided hook.""" + hook.register(self) + if self.hooksEnabled is None: + # A special optimization so that hooks can be effectively + # disabled until one is added or they are explicitly turned on. + self.hooksEnabled = True + if prepend: + self.hooks.insert(0, hook) + else: + self.hooks.append(hook) + + def deregister(self, hook): + """Remove an already registered hook.""" + hook.deregister(self) + self.hooks.remove(hook) + + def invoke(self, _name, **keywords): + """Invoke the hook(s) associated with the hook name, should they + exist.""" + if self.hooksEnabled: + for hook in self.hooks: + hook.push() + try: + method = getattr(hook, _name) + method(*(), **keywords) + finally: + hook.pop() + + def finalize(self): + """Execute any remaining final routines.""" + self.push() + self.invoke('atFinalize') + try: + # Pop them off one at a time so they get executed in reverse + # order and we remove them as they're executed in case something + # bad happens. + while self.finals: + final = self.finals.pop() + final() + finally: + self.pop() + + # Error handling. + + def meta(self, exc=None): + """Construct a MetaError for the interpreter's current state.""" + return MetaError(self.contexts.clone(), exc) + + def handle(self, meta): + """Handle a MetaError.""" + first = True + self.invoke('atHandle', meta=meta) + for context in meta.contexts: + if first: + if meta.exc is not None: + desc = "error: %s: %s" % (meta.exc.__class__, meta.exc) + else: + desc = "error" + else: + desc = "from this context" + first = False + sys.stderr.write('%s: %s\n' % (context, desc)) + + def installProxy(self): + """Install a proxy if necessary.""" + # Unfortunately, there's no surefire way to make sure that installing + # a sys.stdout proxy is idempotent, what with different interpreters + # running from different modules. The best we can do here is to try + # manipulating the proxy's test function ... + try: + sys.stdout._testProxy() + except AttributeError: + # ... if the current stdout object doesn't have one, then check + # to see if we think _this_ particularly Interpreter class has + # installed it before ... + if Interpreter._wasProxyInstalled: + # ... and if so, we have a proxy problem. + raise Error("interpreter stdout proxy lost") + else: + # Otherwise, install the proxy and set the flag. + sys.stdout = ProxyFile(sys.stdout) + Interpreter._wasProxyInstalled = True + + # + # Pseudomodule routines. + # + + # Identification. + + def identify(self): + """Identify the topmost context with a 2-tuple of the name and + line number.""" + return self.context().identify() + + def atExit(self, callable): + """Register a function to be called at exit.""" + self.finals.append(callable) + + # Context manipulation. + + def pushContext(self, name='', line=0): + """Create a new context and push it.""" + self.contexts.push(Context(name, line)) + + def popContext(self): + """Pop the top context.""" + self.contexts.pop() + + def setContextName(self, name): + """Set the name of the topmost context.""" + context = self.context() + context.name = name + + def setContextLine(self, line): + """Set the name of the topmost context.""" + context = self.context() + context.line = line + + setName = setContextName # DEPRECATED + setLine = setContextLine # DEPRECATED + + # Globals manipulation. + + def getGlobals(self): + """Retrieve the globals.""" + return self.globals + + def setGlobals(self, globals): + """Set the globals to the specified dictionary.""" + self.globals = globals + self.fix() + + def updateGlobals(self, otherGlobals): + """Merge another mapping object into this interpreter's globals.""" + self.update(otherGlobals) + + def clearGlobals(self): + """Clear out the globals with a brand new dictionary.""" + self.clear() + + def saveGlobals(self, deep=True): + """Save a copy of the globals off onto the history stack.""" + self.save(deep) + + def restoreGlobals(self, destructive=True): + """Restore the most recently saved copy of the globals.""" + self.restore(destructive) + + # Hook support. + + def areHooksEnabled(self): + """Return whether or not hooks are presently enabled.""" + if self.hooksEnabled is None: + return True + else: + return self.hooksEnabled + + def enableHooks(self): + """Enable hooks.""" + self.hooksEnabled = True + + def disableHooks(self): + """Disable hooks.""" + self.hooksEnabled = False + + def getHooks(self): + """Get the current hooks.""" + return self.hooks[:] + + def clearHooks(self): + """Clear all hooks.""" + self.hooks = [] + + def addHook(self, hook, prepend=False): + """Add a new hook; optionally insert it rather than appending it.""" + self.register(hook, prepend) + + def removeHook(self, hook): + """Remove a preexisting hook.""" + self.deregister(hook) + + def invokeHook(self, _name, **keywords): + """Manually invoke a hook.""" + self.invoke(*(_name,), **keywords) + + # Callbacks. + + def getCallback(self): + """Get the callback registered with this interpreter, or None.""" + return self.callback + + def registerCallback(self, callback): + """Register a custom markup callback with this interpreter.""" + self.callback = callback + + def deregisterCallback(self): + """Remove any previously registered callback with this interpreter.""" + self.callback = None + + def invokeCallback(self, contents): + """Invoke the callback.""" + if self.callback is None: + if self.options.get(CALLBACK_OPT, False): + raise Error("custom markup invoked with no defined callback") + else: + self.callback(contents) + + # Pseudomodule manipulation. + + def flatten(self, keys=None): + """Flatten the contents of the pseudo-module into the globals + namespace.""" + if keys is None: + keys = list(self.__dict__.keys()) + list(self.__class__.__dict__.keys()) + dict = {} + for key in keys: + # The pseudomodule is really a class instance, so we need to + # fumble use getattr instead of simply fumbling through the + # instance's __dict__. + dict[key] = getattr(self, key) + # Stomp everything into the globals namespace. + self.globals.update(dict) + + # Prefix. + + def getPrefix(self): + """Get the current prefix.""" + return self.prefix + + def setPrefix(self, prefix): + """Set the prefix.""" + self.prefix = prefix + + # Diversions. + + def stopDiverting(self): + """Stop any diverting.""" + self.stream().revert() + + def createDiversion(self, name): + """Create a diversion (but do not divert to it) if it does not + already exist.""" + self.stream().create(name) + + def retrieveDiversion(self, name): + """Retrieve the diversion object associated with the name.""" + return self.stream().retrieve(name) + + def startDiversion(self, name): + """Start diverting to the given diversion name.""" + self.stream().divert(name) + + def playDiversion(self, name): + """Play the given diversion and then purge it.""" + self.stream().undivert(name, True) + + def replayDiversion(self, name): + """Replay the diversion without purging it.""" + self.stream().undivert(name, False) + + def purgeDiversion(self, name): + """Eliminate the given diversion.""" + self.stream().purge(name) + + def playAllDiversions(self): + """Play all existing diversions and then purge them.""" + self.stream().undivertAll(True) + + def replayAllDiversions(self): + """Replay all existing diversions without purging them.""" + self.stream().undivertAll(False) + + def purgeAllDiversions(self): + """Purge all existing diversions.""" + self.stream().purgeAll() + + def getCurrentDiversion(self): + """Get the name of the current diversion.""" + return self.stream().currentDiversion + + def getAllDiversions(self): + """Get the names of all existing diversions.""" + names = sorted(self.stream().diversions.keys()) + return names + + # Filter. + + def resetFilter(self): + """Reset the filter so that it does no filtering.""" + self.stream().install(None) + + def nullFilter(self): + """Install a filter that will consume all text.""" + self.stream().install(0) + + def getFilter(self): + """Get the current filter.""" + filter = self.stream().filter + if filter is self.stream().file: + return None + else: + return filter + + def setFilter(self, shortcut): + """Set the filter.""" + self.stream().install(shortcut) + + def attachFilter(self, shortcut): + """Attach a single filter to the end of the current filter chain.""" + self.stream().attach(shortcut) + + +class Document: + + """A representation of an individual EmPy document, as used by a + processor.""" + + def __init__(self, ID, filename): + self.ID = ID + self.filename = filename + self.significators = {} + + +class Processor: + + """An entity which is capable of processing a hierarchy of EmPy + files and building a dictionary of document objects associated + with them describing their significator contents.""" + + DEFAULT_EMPY_EXTENSIONS = ('.em',) + SIGNIFICATOR_RE = re.compile(SIGNIFICATOR_RE_STRING) + + def __init__(self, factory=Document): + self.factory = factory + self.documents = {} + + def identifier(self, pathname, filename): return filename + + def clear(self): + self.documents = {} + + def scan(self, basename, extensions=DEFAULT_EMPY_EXTENSIONS): + if isinstance(extensions, _str): + extensions = (extensions,) + def _noCriteria(x): + return True + def _extensionsCriteria(pathname, extensions=extensions): + if extensions: + for extension in extensions: + if pathname[-len(extension):] == extension: + return True + return False + else: + return True + self.directory(basename, _noCriteria, _extensionsCriteria, None) + self.postprocess() + + def postprocess(self): + pass + + def directory(self, basename, dirCriteria, fileCriteria, depth=None): + if depth is not None: + if depth <= 0: + return + else: + depth -= 1 + filenames = os.listdir(basename) + for filename in filenames: + pathname = os.path.join(basename, filename) + if os.path.isdir(pathname): + if dirCriteria(pathname): + self.directory(pathname, dirCriteria, fileCriteria, depth) + elif os.path.isfile(pathname): + if fileCriteria(pathname): + documentID = self.identifier(pathname, filename) + document = self.factory(documentID, pathname) + self.file(document, open(pathname)) + self.documents[documentID] = document + + def file(self, document, file): + while True: + line = file.readline() + if not line: + break + self.line(document, line) + + def line(self, document, line): + match = self.SIGNIFICATOR_RE.search(line) + if match: + key, valueS = match.groups() + valueS = valueS.strip() + if valueS: + value = eval(valueS) + else: + value = None + document.significators[key] = value + + +def expand(_data, _globals=None, + _argv=None, _prefix=DEFAULT_PREFIX, _pseudo=None, _options=None, \ + **_locals): + """Do an atomic expansion of the given source data, creating and + shutting down an interpreter dedicated to the task. The sys.stdout + object is saved off and then replaced before this function + returns.""" + if len(_locals) == 0: + # If there were no keyword arguments specified, don't use a locals + # dictionary at all. + _locals = None + output = NullFile() + interpreter = Interpreter(output, argv=_argv, prefix=_prefix, + pseudo=_pseudo, options=_options, + globals=_globals) + if interpreter.options.get(OVERRIDE_OPT, True): + oldStdout = sys.stdout + try: + result = interpreter.expand(_data, _locals) + finally: + interpreter.shutdown() + if _globals is not None: + interpreter.unfix() # remove pseudomodule to prevent clashes + if interpreter.options.get(OVERRIDE_OPT, True): + sys.stdout = oldStdout + return result + +def environment(name, default=None): + """Get data from the current environment. If the default is True + or False, then presume that we're only interested in the existence + or non-existence of the environment variable.""" + if name in os.environ: + # Do the True/False test by value for future compatibility. + if default == False or default == True: + return True + else: + return os.environ[name] + else: + return default + +def info(table): + DEFAULT_LEFT = 28 + maxLeft = 0 + maxRight = 0 + for left, right in table: + if len(left) > maxLeft: + maxLeft = len(left) + if len(right) > maxRight: + maxRight = len(right) + FORMAT = ' %%-%ds %%s\n' % max(maxLeft, DEFAULT_LEFT) + for left, right in table: + if right.find('\n') >= 0: + for right in right.split('\n'): + sys.stderr.write(FORMAT % (left, right)) + left = '' + else: + sys.stderr.write(FORMAT % (left, right)) + +def usage(verbose=True): + """Print usage information.""" + programName = sys.argv[0] + def warn(line=''): + sys.stderr.write("%s\n" % line) + warn("""\ +Usage: %s [options] [ [...]] +Welcome to EmPy version %s.""" % (programName, __version__)) + warn() + warn("Valid options:") + info(OPTION_INFO) + if verbose: + warn() + warn("The following markups are supported:") + info(MARKUP_INFO) + warn() + warn("Valid escape sequences are:") + info(ESCAPE_INFO) + warn() + warn("The %s pseudomodule contains the following attributes:" % DEFAULT_PSEUDOMODULE_NAME) + info(PSEUDOMODULE_INFO) + warn() + warn("The following environment variables are recognized:") + info(ENVIRONMENT_INFO) + warn() + warn(USAGE_NOTES) + else: + warn() + warn("Type %s -H for more extensive help." % programName) + +def invoke(args): + """Run a standalone instance of an EmPy interpeter.""" + # Initialize the options. + _output = None + _options = {BUFFERED_OPT: environment(BUFFERED_ENV, False), + RAW_OPT: environment(RAW_ENV, False), + EXIT_OPT: True, + FLATTEN_OPT: environment(FLATTEN_ENV, False), + OVERRIDE_OPT: not environment(NO_OVERRIDE_ENV, False), + CALLBACK_OPT: False} + _preprocessing = [] + _prefix = environment(PREFIX_ENV, DEFAULT_PREFIX) + _pseudo = environment(PSEUDO_ENV, None) + _interactive = environment(INTERACTIVE_ENV, False) + _extraArguments = environment(OPTIONS_ENV) + _binary = -1 # negative for not, 0 for default size, positive for size + _unicode = environment(UNICODE_ENV, False) + _unicodeInputEncoding = environment(INPUT_ENCODING_ENV, None) + _unicodeOutputEncoding = environment(OUTPUT_ENCODING_ENV, None) + _unicodeInputErrors = environment(INPUT_ERRORS_ENV, None) + _unicodeOutputErrors = environment(OUTPUT_ERRORS_ENV, None) + _hooks = [] + _pauseAtEnd = False + _relativePath = False + if _extraArguments is not None: + _extraArguments = _extraArguments.split() + args = _extraArguments + args + # Parse the arguments. + pairs, remainder = getopt.getopt(args, 'VhHvkp:m:frino:a:buBP:I:D:E:F:', ['version', 'help', 'extended-help', 'verbose', 'null-hook', 'suppress-errors', 'prefix=', 'no-prefix', 'module=', 'flatten', 'raw-errors', 'interactive', 'no-override-stdout', 'binary', 'chunk-size=', 'output=' 'append=', 'preprocess=', 'import=', 'define=', 'execute=', 'execute-file=', 'buffered-output', 'pause-at-end', 'relative-path', 'no-callback-error', 'no-bangpath-processing', 'unicode', 'unicode-encoding=', 'unicode-input-encoding=', 'unicode-output-encoding=', 'unicode-errors=', 'unicode-input-errors=', 'unicode-output-errors=']) + for option, argument in pairs: + if option in ('-V', '--version'): + sys.stderr.write("%s version %s\n" % (__program__, __version__)) + return + elif option in ('-h', '--help'): + usage(False) + return + elif option in ('-H', '--extended-help'): + usage(True) + return + elif option in ('-v', '--verbose'): + _hooks.append(VerboseHook()) + elif option in ('--null-hook',): + _hooks.append(Hook()) + elif option in ('-k', '--suppress-errors'): + _options[EXIT_OPT] = False + _interactive = True # suppress errors implies interactive mode + elif option in ('-m', '--module'): + _pseudo = argument + elif option in ('-f', '--flatten'): + _options[FLATTEN_OPT] = True + elif option in ('-p', '--prefix'): + _prefix = argument + elif option in ('--no-prefix',): + _prefix = None + elif option in ('-r', '--raw-errors'): + _options[RAW_OPT] = True + elif option in ('-i', '--interactive'): + _interactive = True + elif option in ('-n', '--no-override-stdout'): + _options[OVERRIDE_OPT] = False + elif option in ('-o', '--output'): + _output = argument, 'w', _options[BUFFERED_OPT] + elif option in ('-a', '--append'): + _output = argument, 'a', _options[BUFFERED_OPT] + elif option in ('-b', '--buffered-output'): + _options[BUFFERED_OPT] = True + elif option in ('-B',): # DEPRECATED + _options[BUFFERED_OPT] = True + elif option in ('--binary',): + _binary = 0 + elif option in ('--chunk-size',): + _binary = int(argument) + elif option in ('-P', '--preprocess'): + _preprocessing.append(('pre', argument)) + elif option in ('-I', '--import'): + for module in argument.split(','): + module = module.strip() + _preprocessing.append(('import', module)) + elif option in ('-D', '--define'): + _preprocessing.append(('define', argument)) + elif option in ('-E', '--execute'): + _preprocessing.append(('exec', argument)) + elif option in ('-F', '--execute-file'): + _preprocessing.append(('file', argument)) + elif option in ('-u', '--unicode'): + _unicode = True + elif option in ('--pause-at-end',): + _pauseAtEnd = True + elif option in ('--relative-path',): + _relativePath = True + elif option in ('--no-callback-error',): + _options[CALLBACK_OPT] = True + elif option in ('--no-bangpath-processing',): + _options[BANGPATH_OPT] = False + elif option in ('--unicode-encoding',): + _unicodeInputEncoding = _unicodeOutputEncoding = argument + elif option in ('--unicode-input-encoding',): + _unicodeInputEncoding = argument + elif option in ('--unicode-output-encoding',): + _unicodeOutputEncoding = argument + elif option in ('--unicode-errors',): + _unicodeInputErrors = _unicodeOutputErrors = argument + elif option in ('--unicode-input-errors',): + _unicodeInputErrors = argument + elif option in ('--unicode-output-errors',): + _unicodeOutputErrors = argument + # Set up the Unicode subsystem if required. + if (_unicode or + _unicodeInputEncoding or _unicodeOutputEncoding or + _unicodeInputErrors or _unicodeOutputErrors): + theSubsystem.initialize(_unicodeInputEncoding, + _unicodeOutputEncoding, + _unicodeInputErrors, _unicodeOutputErrors) + # Now initialize the output file if something has already been selected. + if _output is not None: + _output = AbstractFile(*_output) + # Set up the main filename and the argument. + if not remainder: + remainder.append('-') + filename, arguments = remainder[0], remainder[1:] + # Set up the interpreter. + if _options[BUFFERED_OPT] and _output is None: + raise ValueError("-b only makes sense with -o or -a arguments") + if _prefix == 'None': + _prefix = None + if (_prefix and isinstance(_prefix, _str) and len(_prefix) != 1): + raise Error("prefix must be single-character string") + interpreter = Interpreter(output=_output, + argv=remainder, + prefix=_prefix, + pseudo=_pseudo, + options=_options, + hooks=_hooks) + try: + # Execute command-line statements. + i = 0 + for which, thing in _preprocessing: + if which == 'pre': + command = interpreter.file + target = theSubsystem.open(thing, 'r') + name = thing + elif which == 'define': + command = interpreter.string + if thing.find('=') >= 0: + target = '%s{%s}' % (_prefix, thing) + else: + target = '%s{%s = None}' % (_prefix, thing) + name = '' % i + elif which == 'exec': + command = interpreter.string + target = '%s{%s}' % (_prefix, thing) + name = '' % i + elif which == 'file': + command = interpreter.string + name = '' % (i, thing) + target = '%s{exec(open("""%s""").read())}' % (_prefix, thing) + elif which == 'import': + command = interpreter.string + name = '' % i + target = '%s{import %s}' % (_prefix, thing) + else: + assert 0 + interpreter.wrap(command, (target, name)) + i += 1 + # Now process the primary file. + interpreter.ready() + if filename == '-': + if not _interactive: + name = '' + path = '' + file = sys.stdin + else: + name, file = None, None + else: + name = filename + file = theSubsystem.open(filename, 'r') + path = os.path.split(filename)[0] + if _relativePath: + sys.path.insert(0, path) + if file is not None: + if _binary < 0: + interpreter.wrap(interpreter.file, (file, name)) + else: + chunkSize = _binary + interpreter.wrap(interpreter.binary, (file, name, chunkSize)) + # If we're supposed to go interactive afterwards, do it. + if _interactive: + interpreter.interact() + finally: + interpreter.shutdown() + # Finally, if we should pause at the end, do it. + if _pauseAtEnd: + try: + _input() + except EOFError: + pass + +def main(): + invoke(sys.argv[1:]) + +if __name__ == '__main__': main() diff --git a/sample.bench b/sample.bench new file mode 100644 index 0000000..6afd966 --- /dev/null +++ b/sample.bench @@ -0,0 +1,164 @@ +#! This line however will appear (not the first line in the script). +This is text. It should appear in the processed output. +This is a literal at sign: @. +This is a line continuation; this will appear on the same line. +Note that it will actually eat any whitespace (one word). + +This will appear on one line. +This will appear on a separate line. +This is separated by a tab: See? +These are uppercase As (presuming ASCII): A, A, A, A, A. +This is more text. + +The basics: The square of 4 is 16, or 16. +Internal whitespace is fine: 4 squared is 16. +Statements: 4**2 = 16. +Whitespace too: 4**2 = 16 (still). +But only on single-line statement expansions. +Internal whitespace on multi-line statements is significant. +Normal Python indentation rules must be followed here. +Normal Python indentation rules must be followed here. +Simple expressions: x is 4, l is [3, 2, 1], s is "alpha," and 4 squared is 16. +Literals too: x is 4, but would be written @x. +Trailing dots are ignored: The value of x is 4. +Quotes outside of expansions are also ignored: This is quoted: "x is 4." +Array subscription: The first element of l is 3. +But this is not: The first element of l is not [3, 2, 1] [0]. +That was equivalent to: [3, 2, 1] and then [0], not 3. +But whitespace can go inside the brackets: 3 is 3. +Same with functions: 16 is 16. +The same applies to the other forms. +Involved: The contained value is 3. +More involved: The square of the contained value is 9. +Following expressions: Pluralize "book" as "books," or maybe "books." +By default str is used (alpha), but you can use repr if you want ('alpha'). +Conditional expressions: x is true. +Pluralization: How many words? 4 words. +Protected expressions: foo is not defined. +Also here, whitespace isn't important: bar isn't defined either. +The math module has been imported. +The re module has not been imported. +Division by zero is illegal. +To swallow errors, use None: [two spaces]. +This is self-expanding: @:2 + 2:4: +You can expand multiple times: @:2 + 2:4: + +c's class is C. +c's name is empy. +Method call: Hello, empy. +Note that None is not expanded: [two spaces]. +But the string 'None' is, of course, printed fine: None. +So a function can return None for side effects only: Hello, empy. + +If: a is positive. +If/else: b is negative. +If/elif/else: cmp(a, b) is positive. +Numbers: 0 1 2 3 4 5 6 7 8 9. +Evens: 0 2 4 6 8. +Integers less than 5: 0 1 2 3 4. +Countdown: 10 9 8 7 6 5 4 3 2 1 0. +While/else: works. +For/else: 0 1 2 also works. +Tuple unpacking: <1> <2> <3> <4>. +Tuple unpacking: <1> <2> <3> <4>. +Tuple unpacking: <1> <2> <3> <4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +Tuple unpacking: <1, 2> <3, 4>. +More tuple unpacking: <1, 2> <3, 4>. +Garbage is not defined. +Division by zero is illegal. +Catch all: something happened. +Finally works: finally, and caught. +Define: 5 is positive, -3 is negative, 0 is zero. + +A. This text is undiverted. +B. This text is also undiverted. +C. This text is diverted. +D. Again, this text is undiverted. +E. This text is diverted and then undiverted. +E. This text is diverted and then undiverted (this should appear twice). +F. This text is diverted and then cancelled. +G. This text is again undiverted. +H. There should be one remaining diversion: ['x']. +I. But not after purging it: []. +J. This should be the final diversion, created manually. + +Blanks: , , , . +Single quotes: ', ', '. +Double quotes: ", ", ". +Triple quotes: """, """, ''', '''. +Quotes surrounded by spaces: " , ' . +At signs: @, @, @, @. +Close parentheses: ), ), ), ). +Close parentheses in quotes: ')', ')'. +Close braces with an intervening space: } }. +Repr of a backquote: '`'. +Exes: x, x, x, x, x. +Dollar signs: $, $, $. +These are strings: +single quoted string +double quoted string +single quoted string with escaped 'single quotes' +double quoted string with escaped "double quotes" +triple single quoted string +triple double quoted string +single quoted string with "double quotes" +double quoted string with 'single quotes' +triple single quoted continued string +triple double quoted continued string +triple single quoted +...multi-line string +triple double quoted +... multi-line string + +Encountered significators: +a and b should be None: None, None +c and d should be 'x': 'x', 'x' +e and f should be 'x y': 'x y', 'x y' + +This line should be in mixed case. +this line should be all lowercase. +THIS LINE SHOULD BE ALL UPPERCASE (HOW GAUCHE). +[This line should be bracketed.] +[So should this line.] +*There* shou*ld be* star*s eve*ry fi*ve ch*aract*ers o*n thi*s lin*e. +This line should be back to mixed case. +[THIS LINE SHOULD BE ALL UPPERCASE WITH BRACKETS.] +This line should be back to mixed case (again). + +The new context is sample.em:264. +File inclusion [sample.em:265]: 2 + 2 = 4 [<>:1]. +Expansion [sample.em:266]: This should be appear [:1] on the same line as this [sample.em:268]. +More expansion [sample.em:269]: Another expansion [:1]. +This is the next line [sample.em:271]. +Quoting: x when quoted would be '@x' or @@x. +More quoting: This will be @@doubled but '''@this is not'''. +Here's the last view of the old context: sample.em:274. +Creating a new context ... +The current context is: :1. +The context name should now be 'NewName': NewName:3. +The line number should now be 1000: NewName:1000. +Back to the old context: sample.em:277. + +Interpreter's q is 1. +Embedded interpreter's q is 10. +Interpreter's q is still 1; the embedded interpreter had no effect. +Standalone expansion: 1 + 1 is 2. +With locals: 2 + 3 is 5. +With globals: g's x is 10. +Still with globals: g's x + 1 is 11. +g's x is still 10. + +Invoking the sample hook: [SampleHook.null invoked]. + +Using a custom markup: [This appears in brackets]. +Again: []. +Once more: [This is a right angle bracket in quotes: ">"]. + +This is the penultimate line. diff --git a/sample.em b/sample.em new file mode 100644 index 0000000..0e385c3 --- /dev/null +++ b/sample.em @@ -0,0 +1,336 @@ +#! $Id: sample.em 5359 2014-01-23 00:33:57Z max $ $Date: 2014-01-22 16:33:57 -0800 (Wed, 22 Jan 2014) $ +@# Bangpaths are handled properly (the above line will not appear). +#! This line however will appear (not the first line in the script). +@# This is a comment. This should not appear in the processed output. +This is text. It should appear in the processed output. +This is a literal at sign: @@. +This is a line continuation; @ +this will appear on the same line. +Note that it will actually eat any white@ space (one word). +@{ +# The em.py script has to be somewhere in sys.path! +import em +}@ + +@# Escape codes. +This will appear on one line.@\nThis will appear on a separate line. +This is separated by a tab:@\tSee? +These are uppercase As (presuming ASCII): A, @\q1001, @\o101, @\d065, @\x41. +@{ +import sys +# This is just a normal Python comment. +print("This is more text.") +}@ +@# Note the @{ ... }@ convention to suppress the newline following the }. +@# Also note that comments are completely tossed: This is not expanded: @(x). + +@# The basics. +@{ +import sys, math +x = 4 +s = 'alpha' +word = "book" +l = [3, 2, 1] +def square(n): + return n**2 +friends = ['Albert', 'Betty', 'Charles', 'Donald'] +class Container(object): + + def __init__(self, value): + self.value = value + + def square(self): + return square(self.value) +c = Container(3) +}@ +The basics: The square of @(x) is @(x**2), or @(square(x)). +Internal whitespace is fine: @( x ) squared is @( square(x) ). +Statements: @{sys.stdout.write("%d**2 = %d" % (x, square(x)))}. +Whitespace too: @{ sys.stdout.write("%d**2 = %d (still)" % (x, square(x))) }. +@{ +print("But only on single-line statement expansions.") +if 1: + print("Internal whitespace on multi-line statements is significant.") +for i in range(2): + print("Normal Python indentation rules must be followed here.") +}@ +Simple expressions: x is @x, l is @l, s is "@s," and @x squared is @square(x). +Literals too: x is @x, but would be written @@x. +Trailing dots are ignored: The value of x is @x. +Quotes outside of expansions are also ignored: This is quoted: "x is @x." +@# Whitespace is important in simple expressions. +Array subscription: The first element of l is @l[0]. +But this is not: The first element of l is not @l [0]. +That was equivalent to: @(l) and then [0], not @l[0]. +But whitespace can go inside the brackets: @l[0] is @l[ 0 ]. +Same with functions: @square(x) is @square( x ). +The same applies to the other forms. +Involved: The contained value is @c.value. +More involved: The square of the contained value is @c.square(). +Following expressions: Pluralize "@word" as "@(word)s," or maybe "@word@ s." +By default str is used (@s), but you can use repr if you want (@`s`). +Conditional expressions: @(x ? "x is true" ! "x is false"). +Pluralization: How many words? @x word@(x != 1 ? 's'). +Protected expressions: @(foo $ "foo is not defined"). +Also here, whitespace isn't important: @(bar$"bar isn't defined either"). +The math module has @(math ? "been imported" $ "not been imported"). +The re module has @(re ? "been imported" $ "not been imported"). +Division by zero is @(x/0 $ "illegal"). +To swallow errors, use None: @(buh $ None) [two spaces]. +This is self-expanding: @:2 + 2:(this will get replaced with 4): +You can expand multiple times: @ +@empy.expand("@empy.expand('@:2 + 2:hugalugahglughalug:')") + +@# More complex examples, including classes. +@{ +class C: + def __init__(self, name): + self.name = name + + def greetString(self): + return "Hello, %s" % self.name + + def printGreeting(self): + sys.stdout.write("Hello, %s" % self.name) # implicit None return + +c = C("empy") +}@ +c's class is @c.__class__.__name__. +c's name is @c.name. +Method call: @c.greetString(). +Note that None is not expanded: @(None) [two spaces]. +But the string 'None' is, of course, printed fine: @('None'). +So a function can return None for side effects only: @c.printGreeting(). + +@# Control. +@{ +a = 5 # something positive +b = -3 # something negative +z = 0 # zero +}@ +If: a is @[if a > 0]positive@[end if]. +If/else: b is @[if b > 0]positive@[else]negative@[end if]. +If/elif/else: cmp(a, b) is @ +@[if a < b]negative@[elif a > b]positive@[else]zero@[end if]. +Numbers:@ +@[for i in range(10)] @i@[end for]. +Evens:@ +@[for i in range(10)]@[if i % 2 == 1]@[continue]@[end if] @i@[end for]. +Integers less than 5:@ +@[for i in range(10)]@[if i >= 5]@[break]@[end if] @i@[end for]. +Countdown:@ +@{j = 10}@[while j >= 0] @j@{j = j - 1}@[end while]. +While/else: @[while z]shouldn't get here@[else]works@[end while]. +For/else:@ +@[for i in range(3)] @i@[else] also works@[end for]. +Tuple unpacking:@[for x in [1, 2, 3, 4]] <@x>@[end for]. +Tuple unpacking:@[for x, in [[1], [2], [3], [4]]] <@x>@[end for]. +Tuple unpacking:@[for (x,) in [[1], [2], [3], [4]]] <@x>@[end for]. +Tuple unpacking:@[for x, y in [[1, 2], [3, 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for (x), (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for (x, y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for x, (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for x, (y,) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for]. +Tuple unpacking:@[for (x), y in [[1, 2], [3, 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for (x,), y in [[[1], 2], [[3], 4]]] <@x, @y>@[end for]. +Tuple unpacking:@[for (x,), (y,) in [[[1], [2]], [[3], [4]]]] <@x, @y>@[end for]. +More tuple unpacking:@[for (((x))), ((y),) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for]. +Garbage is @[try]@hglhagulahguha@[except NameError]not defined@[end try]. +Division by zero is @[try]@(a/z)@[except ZeroDivisionError]illegal@[end try]. +Catch all: @[try]@ghlaghlhagl@[except]something happened@[end try]. +Finally works: @ +@[try]@[try]@(1/0)@[finally]finally, @[end try]@[except]and caught@[end try]. +@[def sign(x)]@x is @[if x > 0]positive@[elif x < 0]negative@[else]zero@[end if]@[end def]@ +Define: @sign(a), @sign(b), @sign(z). + +@# Diversions. Again, a trailing @ is used to suppress the following newline. +A. This text is undiverted. +@empy.startDiversion(1)@ +C. This text is diverted. +@empy.stopDiverting()@ +B. This text is also undiverted. +@empy.playDiversion(1)@ +D. Again, this text is undiverted. +@empy.startDiversion('a')@ +E. This text is diverted and then undiverted@ +@empy.stopDiverting()@ +@empy.replayDiversion('a'). +@empy.playDiversion('a') (this should appear twice). +@empy.startDiversion('q')@ +F. This text is diverted and then cancelled. +@empy.playDiversion('q')@ +G. This text is again undiverted. +@empy.startDiversion('x')@ +X. This text will be purged and should not appear! +@empy.stopDiverting()@ +H. There should be one remaining diversion: @empy.getAllDiversions(). +@empy.purgeDiversion('x')@ +I. But not after purging it: @empy.getAllDiversions(). +@{ +# Finally, make a manual diversion and manipulate it. +empy.createDiversion('z') +zDiversion = empy.retrieveDiversion('z') +zDiversion.write("J. This should be the final diversion, created manually.\n") +empy.playDiversion('z') +}@ + +@# Parsing checks. +Blanks: @(''), @(""), @(''''''), @(""""""). +Single quotes: @('\''), @("'"), @("""'"""). +Double quotes: @("\""), @('"'), @('''"'''). +Triple quotes: @("\"\"\""), @('"""'), @('\'\'\''), @("'''"). +Quotes surrounded by spaces: @(""" " """), @(''' ' '''). +At signs: @('@'), @("@"), @('''@'''), @("""@"""). +Close parentheses: @(')'), @(")"), @((")")), @((')')). +Close parentheses in quotes: @("')'"), @('\')\''). +Close braces with an intervening space: @ +@{sys.stdout.write("}")} @{sys.stdout.write('}')}. +Repr of a backquote: @`'`'`. +Exes: @("?"?'x'), @(0?"!"!'x'), @(0?":":'x'), @("]"?'x'), @(1?"x"!"]"). +Dollar signs: @("$"$None), @(asdf?"$"$"$"), @(1?asdf$"$"). +These are strings: +@'single quoted string' +@"double quoted string" +@'single quoted string with escaped \'single quotes\'' +@"double quoted string with escaped \"double quotes\"" +@'''triple single quoted string''' +@"""triple double quoted string""" +@'single quoted string with "double quotes"' +@"double quoted string with 'single quotes'" +@'''triple single quoted continued \ +string''' +@"""triple double quoted continued \ +string""" +@'''triple single quoted +...multi-line string''' +@"""triple double quoted +... multi-line string""" + +@# Significators. +@%a +@%b +@%c "x" +@%d "x" +@%e "x y" +@%f "x y" +Encountered significators: +a and b should be None: @`__a__`, @`__b__` +c and d should be 'x': @`__c__`, @`__d__` +e and f should be 'x y': @`__e__`, @`__f__` + +@# Filters. +This line should be in mixed case. +@empy.setFilter(lambda x: x.lower())@ +This line should be all lowercase. +@empy.setFilter(lambda x: x.upper())@ +This line should be all uppercase (how gauche). +@empy.setFilter([em.LineBufferedFilter(), lambda x: '[%s]\n' % x[:-1]])@ +This line should be bracketed. +So should this line. +@empy.setFilter([em.SizeBufferedFilter(5), lambda x: '*' + x])@ +There should be stars every five characters on this line. +@empy.nullFilter()@ +This line should not appear at all! +@empy.resetFilter()@ +This line should be back to mixed case. +@empy.attachFilter(lambda x: x.upper())@ +@empy.attachFilter(em.LineBufferedFilter())@ +@empy.attachFilter(lambda x: '[%s]\n' % x[:-1])@ +This line should be all uppercase with brackets. +@empy.resetFilter()@ +This line should be back to mixed case (again). + +@# Contexts, metaoperations. +@{ +class FakeFile(object): + + def __init__(self, line): + self.lines = [line] + + def read(self): + return '\n'.join(self.lines) + '\n' + + def readline(self): + if self.lines: + return self.lines.pop(0) + else: + return '' + + def close(self): pass +def context(): + return "%s:%d" % empy.identify() +stringFile = FakeFile("2 + 2 = @(2 + 2) [@context()].\n") +}@ +The new context is @context(). +File inclusion [@context()]: @empy.include(stringFile)@ +Expansion [@context()]: @ +@empy.expand("This should be appear [@context()]") @ +on the same line as this [@context()]. +More expansion [@context()]: @ +@{sys.stdout.write(empy.expand("Another expansion [@context()]"))}. +This is the next line [@context()]. +Quoting: @empy.quote("x when quoted would be '@x' or @x"). +More quoting: @empy.quote("This will be @doubled but '''@this is not'''"). +Here's the last view of the old context: @context(). +Creating a new context ... +@empy.pushContext()@ +The current context is: @context(). +@?NewName +The context name should now be 'NewName': @context(). +@!1000 +The line number should now be 1000: @context(). +@empy.popContext()@ +Back to the old context: @context(). + +@# Embedded interpreters and standalone expansion. +@{ +q = 1 +}@ +Interpreter's q is @q. +@{ +try: + i = em.Interpreter() + i.string("@{q = 10}") + i.string("Embedded interpreter's q is @q.\n") +finally: + i.shutdown() +}@ +Interpreter's q is still @q; the embedded interpreter had no effect. +Standalone expansion: @em.expand("1 + 1 is @(1 + 1).") +With locals: @em.expand("@x + @y is @(x + y).", x=2, y=3) +@{ +g = {} +}@ +With globals: @em.expand("@{x = 10}g's x is @x.", g) +Still with globals: @em.expand("g's x + 1 is @(x + 1).", g) +g's x is still @g['x']. + +@# Hooks. +@{ +class SampleHook(em.Hook): + def null(self): + self.interpreter.write('[SampleHook.null invoked]') + +sampleHook = SampleHook() +empy.addHook(sampleHook) +}@ +Invoking the sample hook: @empy.invokeHook('null'). +@{ +empy.removeHook(sampleHook) +}@ + +@# Custom. +@{ +def customCallback(contents, empy=empy): + empy.write('[%s]' % contents) +empy.registerCallback(customCallback) +}@ +Using a custom markup: @. +Again: @<>. +Once more: @">. +@{ +empy.deregisterCallback() +}@ + +@# Finals; note these are evaluated in reverse order. +@empy.atExit(lambda: empy.write("This is the penultimate line.\n"))@ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..522e5ab --- /dev/null +++ b/setup.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# $Id: setup.py.pre 3116 2004-01-14 02:53:02Z max $ $Date: 2004-01-13 18:53:02 -0800 (Tue, 13 Jan 2004) $ + +from distutils.core import setup + +DESCRIPTION = "A templating system for Python." + +LONG_DESCRIPTION = """\ + EmPy is a system for embedding Python expressions and statements + in template text; it takes an EmPy source file, processes it, and + produces output. This is accomplished via expansions, which are + special signals to the EmPy system and are set off by a special + prefix (by default the at sign, '@'). EmPy can expand arbitrary + Python expressions and statements in this way, as well as a + variety of special forms. Textual data not explicitly delimited + in this way is sent unaffected to the output, allowing Python to + be used in effect as a markup language. Also supported are "hook" + callbacks, recording and playback via diversions, and dynamic, + chainable filters. The system is highly configurable via command + line options and embedded commands. +""" + +setup( + name="empy", + version="3.3.2", + author="Erik Max Francis", + author_email="software@alcyone.com", + url="http://www.alcyone.com/software/empy", + license="%LICENSE", + py_modules=["em", "emlib"], + platforms=["unix", "linux", "win32"], + description=DESCRIPTION, + long_description=LONG_DESCRIPTION, +) diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..cb2386a --- /dev/null +++ b/test.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# $Id: test.sh 5359 2014-01-23 00:33:57Z max $ $Date: 2014-01-22 16:33:57 -0800 (Wed, 22 Jan 2014) $ + +if [ $# = 1 ] +then + PYTHON="$1" +else + PYTHON=python +fi + +EMPY=em.py +SAMPLE=sample.em +BENCH=sample.bench + +if $PYTHON -c 'import sys; print(sys.version)' > /dev/null +then + : +else + echo "EmPy was not checked; $PYTHON looks broken." 1>&2 + exit 1 +fi + +if $PYTHON $EMPY $SAMPLE | diff $BENCH - +then + echo "EmPy checks out." 1>&2 +else + echo "EmPy does not check out! Please mail output to author." 1>&2 +fi diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..4772543 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +3.3.2 -- 2.7.4