From 418bb176021361f539fe5de13c6af0b316d8e4be Mon Sep 17 00:00:00 2001 From: Six Date: Sat, 10 Apr 2010 17:05:55 -0400 Subject: Initial import of source. --- .hgignore | 4 + LICENSE | 570 ++++++++++++++++++++++++++++++++++++++ MANIFEST.in | 5 + README | 39 +++ dodai/__init__.py | 100 +++++++ dodai/config/__init__.py | 64 +++++ dodai/config/db/__init__.py | 179 ++++++++++++ dodai/config/db/sa.py | 64 +++++ dodai/config/file.py | 116 ++++++++ dodai/config/log.py | 150 ++++++++++ dodai/config/option.py | 61 ++++ dodai/config/sections.py | 172 ++++++++++++ dodai/db.py | 30 ++ dodai/tools/__init__.py | 16 ++ dodai/tools/himo.py | 362 ++++++++++++++++++++++++ examples/config/config.cfg | 29 ++ examples/config/connect | 29 ++ examples/example_01.py | 69 +++++ examples/example_02.py | 57 ++++ examples/example_03.py | 54 ++++ pavement.py | 116 ++++++++ test/config/connection | 29 ++ test/test_config/config | 19 ++ test/test_config/config.cfg | 67 +++++ test/test_config/test_db.py | 106 +++++++ test/test_config/test_file.py | 101 +++++++ test/test_config/test_init.py | 62 +++++ test/test_config/test_log.py | 147 ++++++++++ test/test_config/test_option.py | 53 ++++ test/test_config/test_sections.py | 114 ++++++++ test/test_dodai.py | 38 +++ 31 files changed, 3022 insertions(+) create mode 100644 .hgignore create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README create mode 100644 dodai/__init__.py create mode 100644 dodai/config/__init__.py create mode 100644 dodai/config/db/__init__.py create mode 100644 dodai/config/db/sa.py create mode 100644 dodai/config/file.py create mode 100644 dodai/config/log.py create mode 100644 dodai/config/option.py create mode 100644 dodai/config/sections.py create mode 100644 dodai/db.py create mode 100644 dodai/tools/__init__.py create mode 100644 dodai/tools/himo.py create mode 100644 examples/config/config.cfg create mode 100644 examples/config/connect create mode 100644 examples/example_01.py create mode 100644 examples/example_02.py create mode 100644 examples/example_03.py create mode 100644 pavement.py create mode 100644 test/config/connection create mode 100644 test/test_config/config create mode 100644 test/test_config/config.cfg create mode 100644 test/test_config/test_db.py create mode 100644 test/test_config/test_file.py create mode 100644 test/test_config/test_init.py create mode 100644 test/test_config/test_log.py create mode 100644 test/test_config/test_option.py create mode 100644 test/test_config/test_sections.py create mode 100644 test/test_dodai.py diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..3b3ba75 --- /dev/null +++ b/.hgignore @@ -0,0 +1,4 @@ +syntax: glob +*.pyc +*~ +*.kpf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2a315df --- /dev/null +++ b/LICENSE @@ -0,0 +1,570 @@ +GNU General Public License version 3 (GPLv3) + +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, 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 them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can +do these things. + +To protect your rights, we need to prevent others from denying you these +rights or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert +copyright on the software, and (2) offer you this License giving you legal +permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. +This is fundamentally incompatible with the aim of protecting users' freedom +to change the software. The systematic pattern of such abuse occurs in the +area of products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. +Each licensee is addressed as “you”. “Licensees” and “recipients” may be +individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a “modified version” of the earlier work or a work +“based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the +Program. + +To “propagate” a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the +extent that it includes a convenient and prominently visible feature that (1) +displays an appropriate copyright notice, and (2) tells the user that there is +no warranty for the work (except to the extent that warranties are provided), +that licensees may convey the work under this License, and how to view a copy +of this License. If the interface presents a list of user commands or options, +such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The “source code” for a work means the preferred form of the work for making +modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The “System Libraries” of an executable work include anything, other than the +work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only +to enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code +form. A “Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as +by intimate data communication or control flow between those subprograms and +other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes it +unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure +under any applicable law fulfilling obligations under article 11 of the WIPO +copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on +each copy an appropriate copyright notice; keep intact all notices stating that +this License and any non-permissive terms added in accord with section 7 apply +to the code; keep intact all notices of the absence of any warranty; and give +all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + + * a) The work must carry prominent notices stating that you modified it, + and giving a relevant date. + * b) The work must carry prominent notices stating that it is released + under this License and any conditions added under section 7. This + requirement modifies the requirement in section 4 to “keep intact all + notices”. + * c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will + therefore apply, along with any applicable section 7 additional terms, + to the whole of the work, and all its parts, regardless of how they + are packaged. This License gives no permission to license the work in + any other way, but it does not invalidate such permission if you have + separately received it. + * d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work + need not make them do so. + +A compilation of a covered work with other separate and independent works, +which are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an “aggregate” if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections +4 and 5, provided that you also convey the machine-readable Corresponding +Source under the terms of this License, in one of these ways: + + * a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium customarily + used for software interchange. + * b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a written + offer, valid for at least three years and valid for as long as you + offer spare parts or customer support for that product model, to give + anyone who possesses the object code either (1) a copy of the + Corresponding Source for all the software in the product that is + covered by this License, on a durable physical medium customarily used + for software interchange, for a price no more than your reasonable + cost of physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no charge. + * c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed + only occasionally and noncommercially, and only if you received the + object code with such an offer, in accord with subsection 6b. + * d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to copy + the object code is a network server, the Corresponding Source may be + on a different server (operated by you or a third party) that supports + equivalent copying facilities, provided you maintain clear directions + next to the object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you remain + obligated to ensure that it is available for as long as needed to + satisfy these requirements. + * e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of + the work are being offered to the general public at no charge under + subsection 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible +personal property which is normally used for personal, family, or household +purposes, or (2) anything designed or sold for incorporation into a dwelling. +In determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a +particular user, “normally used” refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified +version of its Corresponding Source. The information must suffice to ensure +that the continued functioning of the modified object code is in no case +prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added +by you to a covered work, for which you have or can give appropriate copyright +permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + + * a) Disclaiming warranty or limiting liability differently from the terms + of sections 15 and 16 of this License; or + * b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices + displayed by works containing it; or + * c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + * d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + * e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + * f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any + liability that these contractual assumptions directly impose on those + licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” +within the meaning of section 10. If the Program as you received it, or any +part of it, contains a notice stating that it is governed by this License along +with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or +conveying under this License, you may add to a covered work material governed +by the terms of that license document, provided that the further restriction +does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements +apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, +and will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated (a) provisionally, unless and until +the copyright holder explicitly and finally terminates your license, and (b) +permanently, if the copyright holder fails to notify you of the violation by +some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses +of parties who have received copies or rights from you under this License. If +your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy +of the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to +do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. + +An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work +results from an entity transaction, each party to that transaction who receives +a copy of the work also receives whatever licenses to the work the party's +predecessor in interest had or could give under the previous paragraph, plus a +right to possession of the Corresponding Source of the work from the +predecessor in interest, if the predecessor has it or can get it with +reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. + +11. Patents. + +A “contributor” is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent +infringement). To “grant” such a patent license to a party means to make such +an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available +network server or other readily accessible means, then you must either (1) +cause the Corresponding Source to be so available, or (2) arrange to deprive +yourself of the benefit of the patent license for this particular work, or (3) +arrange, in a manner consistent with the requirements of this License, to +extend the patent license to downstream recipients. “Knowingly relying” means +you have actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work in a +country, would infringe one or more identifiable patents in that country that +you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, +you convey, or propagate by procuring conveyance of, a covered work, and grant +a patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients +of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by you (or +copies made from those copies), or (b) primarily for and in connection with +specific products or compilations that contain the covered work, unless you +entered into that arrangement, or that patent license was granted, prior to +28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to +you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If 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 convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to +link or combine any covered work with a work licensed under version 3 of the +GNU Affero General Public License into a single combined work, and to convey +the resulting work. The terms of this License will continue to apply to the +part which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +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 Program specifies +that a certain numbered version of the GNU General Public License “or any later +version” applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. + +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright +holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE +LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM “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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..4e3618d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include setup.py +include pavement.py +include paver-minilib.zip +include LICENSE +prune test diff --git a/README b/README new file mode 100644 index 0000000..cc8e664 --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +DODAI + +A Python module to help with writing command line scripts + +This module is to be a foundation to your command line python +scripts. This module provides for quick access to logger, configparser, +optionparse and databases via sqlalchemy. + + +INSTALLATION +------------ + +There are three ways to install dodai + +1. The easy_install method + + easy_install dodai + +2. To do a traditional install, download compressed file from: + http://code.google.com/p/dodai/downloads/list + + tar zxvf dodai-* + cd dodai-* + python setup.py install + +3. If you have Paver installed then download compressed file from + http://code.google.com/p/dodai/downloads/list + + tar zxvf dodai-* + cd dodai-* + paver install + + +The installation will also install sqlalchemy. This installation will not +install the required system and python libs needed to get sqlalchemy to work +with your desired database. This includes things like: cx_Oracle, psycopg2, +pyodbc, mysql-python etc. For more information on installing these check out +the docs at http://code.google.com/p/dodai + diff --git a/dodai/__init__.py b/dodai/__init__.py new file mode 100644 index 0000000..f1e90bc --- /dev/null +++ b/dodai/__init__.py @@ -0,0 +1,100 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + + +import os +import sys +from dodai.config import Config +from dodai.config.db import ConfigDbFile + + +class Configure(Config): + + CONFIG_FILES = ['config', 'configs', 'configure', 'connect', 'connections', + 'connection'] + + def __init__(self, project_name, config_files=None): + self.config_files = [] + self.project_name = project_name + self.home_directory = self._home_directory() + self._default_files() + self._add_files(config_files) + self._add_config_files() + + def _add_files(self, filenames): + if filenames: + if isinstance(filenames, list) or isinstance(filenames, tuple): + for filename in filenames: + self._add_file(filename) + else: + self._add_file(filenames) + + def _add_file(self, filename): + if os.path.isfile(filename): + if not filename in self.config_files: + self.config_files.append(filename) + + def _default_files(self): + dirs = [] + dirs.append(self._construct_system_config_directory()) + dirs.append(self._construct_user_config_directory()) + dirs.append(self._construct_project_config_directory()) + files = [] + for dir in dirs: + for name in self.CONFIG_FILES: + files.append(os.path.join(dir, name)) + for filename in files: + self._add_file(filename) + + def _home_directory(self): + out = None + try: + from win32com.shell import shellcon, shell + out = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0) + except ImportError: + out = os.path.expanduser("~") + return out + + def _construct_system_config_directory(self): + out = os.path.join('etc', self.project_name.lower()) + return out + + def _construct_user_config_directory(self): + project_directory = ".{dir}".format(dir=self.project_name.lower()) + out = os.path.join(self.home_directory, project_directory) + return out + + def _construct_project_config_directory(self): + dir = os.path.dirname(os.path.abspath(sys.argv[0])) + out = os.path.join(dir, 'config') + return out + + def _add_config_files(self): + if self.config_files: + self.files().add_file(self.config_files) + config = self.files().parser() + if self._has_connections(config): + self.dbs().add_config(config) + + def _has_connections(self, config): + obj = ConfigDbFile(config) + connections = obj() + if connections: + return True + else: + return False diff --git a/dodai/config/__init__.py b/dodai/config/__init__.py new file mode 100644 index 0000000..aac9be7 --- /dev/null +++ b/dodai/config/__init__.py @@ -0,0 +1,64 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + + +class Config(object): + + def __call__(self): + obj = ConfigResults() + if hasattr(self, 'vars'): + for key, val in self.vars.items(): + setattr(obj, key, val) + return obj + + def set(self, key, val): + if not hasattr(self, 'vars'): + self.vars = {} + self.vars[key] = val + + def options(self): + if not hasattr(self, '_options'): + from dodai.config.option import ConfigOption + self._options = ConfigOption() + return self._options + + def files(self): + if not hasattr(self, '_files'): + from dodai.config.file import ConfigFile + self._files = ConfigFile() + return self._files + + def logs(self): + if not hasattr(self, '_logs'): + from dodai.config.log import ConfigLog + self._logs = ConfigLog() + return self._logs + + def dbs(self): + if not hasattr(self, '_dbs'): + from dodai.config.db import ConfigDb + self._dbs = ConfigDb() + return self._dbs + + + + + + +class ConfigResults(object): + pass diff --git a/dodai/config/db/__init__.py b/dodai/config/db/__init__.py new file mode 100644 index 0000000..fa510ac --- /dev/null +++ b/dodai/config/db/__init__.py @@ -0,0 +1,179 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +class ConfigDb(object): + + def __init__(self): + self.connections = {} + self._handlers = {} + from dodai.config.db.sa import Sa + self.register_handler('sa', Sa) + + def register_handler(self, name, obj): + self._handlers[name] = [obj, None] + + def add_config(self, config_parser=None): + if config_parser: + if hasattr(config_parser, 'sections') and \ + hasattr(config_parser, 'options'): + config_obj = ConfigDbFile(config_parser) + self._add_connections(config_obj) + else: + raise NotConfigParserObject() + + def _add_connections(self, config_obj): + connections = config_obj() + for name, obj in connections.items(): + self.connections[name] = obj + + def load(self, name): + if name in self.connections: + connection = self.connections[name] + if connection.db_obj: + return connection.db_obj + else: + handler = self._load_handler(connection.handler) + db_obj = handler.load(connection) + self.connections[name].db_obj = db_obj + return db_obj + + def _load_handler(self, name): + if name in self._handlers: + handler = self._handlers[name] + cls = handler[0] + obj = handler[1] + if not obj: + obj = cls() + self._handlers[name] = [cls, obj] + return obj + raise UnknownHandlerException(name) + + + +class ConfigDbFile(object): + + OPTIONS_REQUIRED = [ + ['protocol', 'hostname', 'port', 'username', 'password','database'], + ['protocol', 'filename'] + ] + OPTIONS_EXTRA = ['protocol_extra', 'handler'] + DEFAULT_HANDLER = 'sa' + + def __init__(self, config_parser): + self.parser = config_parser + self._options = self._all_options() + self.connections = {} + + def __call__(self): + if not self.connections: + for section in self.parser.sections(): + if self._is_valid(section): + obj = self._build_connection(section) + self.connections[obj.name] = obj + return self.connections + + def _all_options(self): + out = [] + for option_group in self.OPTIONS_REQUIRED: + for option in option_group: + if option not in out: + out.append(option) + for option in self.OPTIONS_EXTRA: + if option not in out: + out.append(option) + return out + + def _is_valid(self, section): + for option_group in self.OPTIONS_REQUIRED: + total = len(option_group) + count = 0 + for option in option_group: + if option in self.parser.options(section): + value = self.parser.get(section, option) + if value: + count += 1 + if count >= total: + return True + return False + + def _build_connection(self, section): + obj = ConfigDbConnection() + for option in self._options: + obj.name = section + if self.parser.has_option(section, option): + value = self.parser.get(section, option) + setattr(obj, option, value) + if not hasattr(obj, 'handler') or not obj.handler: + obj.handler = self.DEFAULT_HANDLER + return obj + + +class BaseConfigDb(object): + + PROTOCOLS = ['postgresql', 'mysql', 'sqlite', 'mssql', 'oracle'] + + def _clean(self, obj): + obj.protocol = self._clean_protocol(obj.protocol) + if hasattr(obj, 'port'): + obj.port = self._clean_port(obj.port) + + def _clean_protocol(self, data): + data = data.lower() + if data in ('postgres', 'postgre'): + data = 'postgresql' + if data not in self.PROTOCOLS: + raise InvalidProtocolException(data) + else: + return data + + def _clean_port(self, data): + try: + data = int(data) + except ValueError: + data = None + except TypeError: + data = None + if data: + if data <1 or data > 65535: + raise InvalidPortException(data) + return data + + +class ConfigDbConnection(object): + + def __init__(self): + self.db_obj = None + + +class NotConfigParserObject(Exception): + pass + + +class InvalidProtocolException(Exception): + pass + + +class InvalidPortException(Exception): + pass + + +class UnknownHandlerException(Exception): + pass + + +class UnknownConnectionException(Exception): + pass diff --git a/dodai/config/db/sa.py b/dodai/config/db/sa.py new file mode 100644 index 0000000..06adf9c --- /dev/null +++ b/dodai/config/db/sa.py @@ -0,0 +1,64 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +from dodai.config.db import BaseConfigDb +from dodai.db import Db + +class Sa(BaseConfigDb): + + + def load(self, obj): + from sqlalchemy.orm import sessionmaker + self._clean(obj) + db = Db() + db.engine = self._build_engine(obj) + Session = sessionmaker(bind=db.engine) + db.session = Session() + db.name = obj.name + db.protocol = obj.protocol + if hasattr(obj, 'filename') and obj.filename: + db.filename = obj.filename + else: + db.hostname = obj.hostname + if hasattr(obj, 'port') and obj.port: + db.port = obj.port + db.database = obj.database + return db + + def _build_connection_string(self, obj): + out = [] + out.append('{db.protocol}') + if hasattr(obj, 'protocol_extra') and obj.protocol_extra: + out.append('+{db.protocol_extra}') + out.append('://') + if hasattr(obj, 'filename') and obj.filename: + out.append('{db.filename}') + else: + out.append('{db.username}:{db.password}@') + out.append('{db.hostname}') + if hasattr(obj, 'port') and obj.port: + out.append(':{db.port}') + out.append('/{db.database}') + out = ''.join(out) + out = out.format(db=obj) + return out + + def _build_engine(self, obj): + from sqlalchemy import create_engine + connection_string = self._build_connection_string(obj) + db_obj = create_engine(connection_string) + return db_obj diff --git a/dodai/config/file.py b/dodai/config/file.py new file mode 100644 index 0000000..5cab6f8 --- /dev/null +++ b/dodai/config/file.py @@ -0,0 +1,116 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +import os +import ConfigParser + +class ConfigFile(object): + + def __init__(self): + self._files = [] + self._parser = None + self.files_loaded = [] + self._dir = None + + def set_directory(self, path): + """ Sets the direcory where files will be looked for + raises: InvalidDirectoryException or DirectoryDoesNotExistException + """ + if os.path.isdir(path): + self._dir = path + else: + if os.path.isfile(path): + raise InvalidDirectoryException(path) + else: + raise DirectoryDoesNotExistException(path) + + def get_directory(self): + """ Returns the directory where files will be looked for + """ + return self._dir + + def add_file(self, path): + """ Adds a full file path with the given path (list or string) + raises: FileDoesNotExistException + """ + if isinstance(path, list): + for file_ in path: + self._add_file(file_) + else: + if path not in self._files: + self._add_file(path) + + def _add_file(self, path): + """ Adds the given file path file to the object if the filepath + doesn't already exist + """ + if os.path.isfile(path): + if path not in self._files: + self._files.append(path) + else: + raise FileDoesNotExistException(path) + + def get_files(self): + """ Returns a list of files that were added to this object + """ + return self._files + + def parser(self): + """ Returns a ConfigParser.ConfigParser object with files loaded + raises: NoFilesToLoadException + """ + self._reset_parser() + if not self._parser: + if not self._files: + raise NoFilesToLoadException() + self._parser = ConfigParser.ConfigParser() + self.files_loaded = self._parser.read(self._files) + return self._parser + + def load(self, name): + """ Takes the given name and merges it with the object's directory + then adds the path to the object + """ + if not self._dir: + raise DirectoryNotSetException() + else: + path = os.path.join(self._dir, name) + self.add_file(path) + + def _reset_parser(self): + """ Resets the _parser property if the files_loaded does not equal + the files assigned to this object + """ + if self._parser: + if self.files_loaded != self._files: + self._parser = None + +class NoFilesToLoadException(Exception): + pass + +class DirectoryNotSetException(Exception): + pass + +class InvalidDirectoryException(Exception): + pass + +class DirectoryDoesNotExistException(Exception): + pass + +class FileDoesNotExistException(Exception): + pass diff --git a/dodai/config/log.py b/dodai/config/log.py new file mode 100644 index 0000000..fdb5c93 --- /dev/null +++ b/dodai/config/log.py @@ -0,0 +1,150 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +import logging +import logging.handlers +import os + +class ConfigLog(object): + + LEVELS = { + logging.CRITICAL: [ + 'critical', + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "%(message)s"], + logging.ERROR: [ + 'error', + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "%(message)s"], + logging.WARNING: [ + 'warning', + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "%(message)s"], + logging.INFO: [ + 'info', + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "%(message)s"], + logging.DEBUG: [ + 'debug', + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "%(message)s"] + } + + MAX_BYTES = 10485760 + BACKUP_COUNT = 5 + FILE_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + STDOUT_FORMAT = "%(message)s" + + def __init__(self): + self.log_level = logging.CRITICAL + self.directory = None + self._levels = {} + + def set_log_level(self, level): + try: + level = self._fetch_log_level(level) + except InvalidLevelException: + pass + else: + self.log_level = level + + def set_directory(self, directory): + if os.path.isdir(directory): + self.directory = directory + else: + raise NoDirectoryExistException(directory) + + def get_file_message_format(self, level): + if not self._levels: + self._levels = self.LEVELS + level = self._fetch_log_level(level) + return self._levels[level][1] + + def get_screen_message_format(self, level): + if not self._levels: + self._levels = self.LEVELS + level = self._fetch_log_level(level) + return self._levels[level][2] + + def _fetch_log_level(self, level): + out = None + if isinstance(level, str): + level = level.strip().lower() + if level in self.LEVELS: + out = level + else: + for key, items in self.LEVELS.items(): + if level == items[0]: + out = key + if not out: + raise InvalidLevelException(level) + else: + return out + + def _build_filepath(self, data): + data = os.path.normpath(data) + if data.startswith(os.path.sep): + dir = os.path.dirname(data) + if not os.path.isdir(dir): + raise NoDirectoryExistException(dir) + else: + if not self.directory: + raise DirectoryNotSetException() + else: + data = os.path.join(self.directory, data) + return data + + def load(self, name): + log =logging.getLogger(name) + log.setLevel(self.log_level) + return log + + def attach_file_handler(self, log, filename): + filepath = self._build_filepath(filename) + handler = logging.handlers.RotatingFileHandler( + filepath, maxBytes = self.MAX_BYTES, + backupCount=self.BACKUP_COUNT) + file_format = self.get_file_message_format(self.log_level) + format_obj = logging.Formatter(file_format) + handler.setFormatter(format_obj) + handler.setLevel(self.log_level) + log.addHandler(handler) + + def attach_screen_handler(self, log, level=None): + if level: + level = self._fetch_log_level(level) + else: + level = self.log_level + message_format = self.get_screen_message_format(level) + handler = logging.StreamHandler() + handler.setLevel(level) + format_obj = logging.Formatter(message_format) + handler.setFormatter(format_obj) + log.addHandler(handler) + + +class NoDirectoryExistException(Exception): + pass + + +class DirectoryNotSetException(Exception): + pass + + +class InvalidLevelException(Exception): + pass diff --git a/dodai/config/option.py b/dodai/config/option.py new file mode 100644 index 0000000..0561881 --- /dev/null +++ b/dodai/config/option.py @@ -0,0 +1,61 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +from optparse import OptionParser + +class ConfigOption(object): + + def __init__(self): + + self.parser = OptionParser() + self._options = None + self._args = [] + + def get_args(self): + self._parse_args() + return self._args + + def get_options(self): + self._parse_args() + return self._options + + def _parse_args(self): + options, args = self.parser.parse_args() + self._options = options + self._args = args + + def add_quiet(self): + + self.parser.add_option("-q", "--quiet", dest="verbose", default=True, + action="store_false", + help="Don't print status messages to the screen") + + def add_verbose(self): + self.parser.add_option("-v", "--verbose", dest="verbose", + action="store_true", + default=False, help="Print status messages to the screen") + + def add_log_level(self, default='critical'): + self.parser.add_option("-l", "--log-level", dest="log_level", + default=default, help="Sets the log level") + + def add_setup(self): + self.parser.add_option('', "--setup", dest="setup", + action="store_true", default=False, + help="run the setup which builds the config "\ + "files.") diff --git a/dodai/config/sections.py b/dodai/config/sections.py new file mode 100644 index 0000000..feb59be --- /dev/null +++ b/dodai/config/sections.py @@ -0,0 +1,172 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unicodedata + +class ConfigSections(object): + """ + An iterable object that contains ConfigSection objects + + """ + + def __init__(self, string_object = None): + """ + Iterable object that handles the conversion of a config + parser object to a list of section objects. + + + string_object: This is an object (non instantiated or + callable) that the results of the config's + sections, and options will be stored in. + This enables you to store your values as a + custom object. A good object to use is the + dodai.tools.himo Himo object. If the + string_object is not given the default python + str() object will be used. + + """ + self._string_object = string_object or None + self._sections = {} + + def __call__(self, parser): + """ + Parses the given parser object into this object's sections. + + parser: The actual parser object that is used to + get the sections. This object must have + the sections(), options() and get() + methods. A good object to use is the native + ConfigParse object. However, you can create + your own + + """ + self._build_sections(parser) + + def _build_sections(self, parser): + # Builds ConfigSection objects from the parser object + + for section_name in parser.sections(): + section = self.get_section(section_name) + self._build_options(parser, section_name, section) + + def _build_options(self, parser, section_name, section): + # Adds the options to the section object + + for key in parser.options(section_name): + key = unicode(self._build_string_object(key)) + value = self._build_string_object(parser.get(section_name, key)) + setattr(section, key, value) + + def _build_string_object(self, data): + if self._string_object: + return self._string_object(data) + else: + return data + + def get_section(self, section_name): + """ + Returns a ConfigSection object from this object's section + dictionary or creates a new ConfigSection object, which is + stored int this object's section dictionary then is returned + + """ + section_name = unicode(self._build_string_object(section_name)) + if section_name in self._sections: + return self._sections[section_name] + else: + section = ConfigSection(section_name) + self._sections[section_name] = section + return section + + def __getitem__(self, key): + key = normalize_key(key) + return self._sections[key] + + def __getattr__(self, key): + key = normalize_key(key) + try: + out = self._sections[key] + except KeyError: + return getattr(self._sections, key) + else: + return out + + def __iter__(self, *args, **kargs): + return self._sections.__iter__(*args, **kargs) + + + def __len__(self): + return len(self._sections) + +class ConfigSection(object): + """ + A generic object to hold keys and values primarily from a config file + + """ + def __init__(self, title): + """ + Holds keys and values primarily from a section of a config file + + title: The title of the section of the config file + + """ + self.___title___ = title + self.___options___ = {} + + + def get_title(self): + """ + Returns the title of the section + + """ + return self.___title___ + + + def __setattr__(self, key, value): + if key.startswith('___') and key.endswith('___'): + object.__setattr__(self, key, value) + else: + key = normalize_key(key) + if self.___options___.has_key(key): + self.___options___[key] = value + else: + dict.__setitem__(self.___options___, key, value) + + def __getattr__(self, key): + if key.startswith('___') and key.endswith('___'): + return self.__dict__[key] + else: + key = normalize_key(key) + try: + out = self.___options___[key] + except KeyError: + return getattr(self.___options___, key) + else: + return out + + def __getitem__(self, key): + key = normalize_key(key) + return self.___options___[key] + + def __iter__(self, *args, **kargs): + return self.___options___.__iter__(*args, **kargs) + + +def normalize_key(key): + key = unicode(key) + key = unicodedata.normalize('NFC', key) + return key diff --git a/dodai/db.py b/dodai/db.py new file mode 100644 index 0000000..d65f60a --- /dev/null +++ b/dodai/db.py @@ -0,0 +1,30 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sodai. If not, see . + + +class Db(object): + pass + + def __init__(self): + self.name = None + self.protocol = None + self.hostname = None + self.port = None + self.database = None + self.filename = None + self.engine = None + self.session = None diff --git a/dodai/tools/__init__.py b/dodai/tools/__init__.py new file mode 100644 index 0000000..c54f7f2 --- /dev/null +++ b/dodai/tools/__init__.py @@ -0,0 +1,16 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . diff --git a/dodai/tools/himo.py b/dodai/tools/himo.py new file mode 100644 index 0000000..5a96f91 --- /dev/null +++ b/dodai/tools/himo.py @@ -0,0 +1,362 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import chardet +import re +import sys +import unicodedata +from htmlentitydefs import name2codepoint +from htmlentitydefs import codepoint2name +from decimal import Decimal as D + +class Himo(object): + """ + A unicode-string object with some added features to help with + unicode decoding and output conversions. + """ + + MAP = {169:u'(C)', 174:u'(R)', 8471:u'(P)'} + + def __init__(self, data, encoding=None): + """ + data: Accepts any type of string object (int, float, + string, unicode) + + encoding: Character encoding to help with converting the input + into unicode + + The input data will be converted into an unicode object, unless + the input data is already an unicode object. If the param + 'encoding' is set, the input data will be converted to unicode + using that value. If no 'encoding' is given this object will + attempt to figure out the encoding. First the encoding of the + operating system will be used. If there are any errors, the + chardet module will be used. This object makes no guarantees + that the correct encoding will be detected. + + """ + + self._encoding = encoding or self._system_encoding() + self.data = self._decode(data) + + def ascii(self): + """ + Returns an ascii representation of this object value. + Throws HimoAsciiError if this method was unable to + convert a unicode character down to it's root character. + For example if in your string you have a character + like the letter 'e' but it has an accent mark over it, + this method will convert that character to it's root + character. Thus 'e' with an accent mark over it will + replaced with the regular letter 'e'. + + """ + out = [] + for char in self.data: + if ord(char) < 127: + out.append(char) + elif ord(char) in self.MAP: + out.append(self.MAP[ord(char)]) + else: + num = unicodedata.decomposition(char).split(' ')[0] + if num: + out.append(unichr(int(num, 16))) + else: + print char + raise HimoAsciiError("Unable to convert 'u{0}' "\ + "character to ascii".format(ord(char))) + return str(''.join(out)) + + def html(self): + """ + Returns a unicode string containing this object's value + html enetity encoded. + """ + out = [] + for char in self.data: + out.append(self._html_char_encode(char)) + return ''.join(out) + + def decimal(self): + """ + Returns a decimal object with the value of this object + + """ + + return D(self.data) + + def _decode(self, data): + # Returns a unicode string. If data contains any html encoded + # characters, the characters will be converted to their unicode + # equivalent + + data = self._as_unicode(data) + expression = re.compile(r'&(#?)(x?)(\w+);') + return expression.subn(self._html_decode, data)[0] + + def _as_unicode(self, data): + # Returns string as a unicode string + + if not isinstance(data, unicode): + if not isinstance(data, str): + data = str(data) + try: + data = data.decode(self._encoding) + except UnicodeDecodeError: + info = chardet.detect(data) + self.encoding = info['encoding'] + data = data.decode(info['encoding']) + return unicodedata.normalize('NFC', data) + + def _html_char_encode(self, char): + # Returns an html version of the char + + number = ord(char) + try: + char = "&{0};".format(codepoint2name[number]) + except KeyError: + if number > 127: + char = "&#{0};".format(number) + return char + + def _html_decode(self, values): + # Returns the unicode character from the re.subn + + value = values.group(3) + if values.group(1): + if values.group(2): + return unichr(int('0x{0}'.format(value), 16)) + else: + return unichr(int(value)) + else: + try: + char = name2codepoint[value] + except KeyError: + return values.group() + else: + return unichr(char) + + def _system_encoding(self): + # Returns the character encoding of the system + + encoding = sys.getfilesystemencoding() + if not encoding: + encoding = sys.getdefaultencoding() + return encoding + + #def __cmp__(self, other): + # if self.__eq__(other): + # return 1 + # else: + # pool = [str(self.data), str(other)] + # pool.sort() + # if pool[0] == self.data: + # return -1 + # else: + # return 1 + + + def _is_himo(self, other): + if hasattr(other, '_is_himo'): + return True + return False + + def __len__(self): + return len(self.data) + + def __repr__(self): + return repr(self.data) + + def __str__(self): + return self.data.encode(self._encoding) + + def __iter__(self): + for char in self.data: + yield char + + def __int__(self): + return int(self.data) + + def __float__(self): + return float(self.data) + + def __eq__(self, other): + if self._is_himo(other): + other = other.data + return self.data.__eq__(other) + + def __ne__(self, other): + if self._is_himo(other): + other = other.data + return self.data.__ne__(other) + + def __gt__(self, other): + if self._is_himo(other): + other = other.data + lines = [self.data, other] + lines.sort() + if lines[0] == self.data: + return True + else: + return False + + def __lt__(self, other): + if self._is_himo(other): + other = other.data + lines = [self.data, other] + lines.sort() + if lines[0] != self.data: + return True + else: + return False + + def __cmp__(self, other): + if self.__eq__(other): + return 0 + elif self.__lt__(other): + return -1 + else: + return 1 + + def __unicode__(self): + return self.data + + def capitalize(self, *args, **kargs): + return self.data.capitalize(*args, **kargs) + + def center(self, *args, **kargs): + return self.data.center(*args, **kargs) + + def count(self, *args, **kargs): + return self.data.count(*args, **kargs) + + def decode(self, *args, **kargs): + return self.data.decode(*args, **kargs) + + def encode(self, *args, **kargs): + return self.data.encode(*args, **kargs) + + def encode(self, *args, **kargs): + return self.data.encode(*args, **kargs) + + def endswith(self, *args, **kargs): + return self.data.endswith(*args, **kargs) + + def expandtabs(self, *args, **kargs): + return self.data.expandtabs(*args, **kargs) + + def find(self, *args, **kargs): + return self.data.find(*args, **kargs) + + def format(self, *args, **kargs): + return self.data.format(*args, **kargs) + + def index(self, *args, **kargs): + return self.data.index(*args, **kargs) + + def isalnum(self, *args, **kargs): + return self.data.isalnum(*args, **kargs) + + def isalpha(self, *args, **kargs): + return self.data.isalpha(*args, **kargs) + + def isdecimal(self, *args, **kargs): + return self.data.isdecimal(*args, **kargs) + + def isdigit(self, *args, **kargs): + return self.data.isdigit(*args, **kargs) + + def islower(self, *args, **kargs): + return self.data.islower(*args, **kargs) + + def isnumeric(self, *args, **kargs): + return self.data.isnumeric(*args, **kargs) + + def isspace(self, *args, **kargs): + return self.data.isspace(*args, **kargs) + + def istitle(self, *args, **kargs): + return self.data.istitle(*args, **kargs) + + def isupper(self, *args, **kargs): + return self.data.isupper(*args, **kargs) + + def join(self, *args, **kargs): + return self.data.join(*args, **kargs) + + def ljust(self, *args, **kargs): + return self.data.ljust(*args, **kargs) + + def lower(self, *args, **kargs): + return self.data.lower(*args, **kargs) + + def lstrip(self, *args, **kargs): + return self.data.lstrip(*args, **kargs) + + def partition(self, *args, **kargs): + return self.data.partition(*args, **kargs) + + def replace(self, *args, **kargs): + return self.data.replace(*args, **kargs) + + def rfind(self, *args, **kargs): + return self.data.rfind(*args, **kargs) + + def rindex(self, *args, **kargs): + return self.data.rindex(*args, **kargs) + + def rjust(self, *args, **kargs): + return self.data.rjust(*args, **kargs) + + def rpartition(self, *args, **kargs): + return self.data.rpartition(*args, **kargs) + + def rsplit(self, *args, **kargs): + return self.data.rsplit(*args, **kargs) + + def rstrip(self, *args, **kargs): + return self.data.rstrip(*args, **kargs) + + def split(self, *args, **kargs): + return self.data.split(*args, **kargs) + + def splitlines(self, *args, **kargs): + return self.data.splitlines(*args, **kargs) + + def startswith(self, *args, **kargs): + return self.data.startswith(*args, **kargs) + + def strip(self, *args, **kargs): + return self.data.strip(*args, **kargs) + + def swapcase(self, *args, **kargs): + return self.data.swapcase(*args, **kargs) + + def title(self, *args, **kargs): + return self.data.title(*args, **kargs) + + def translate(self, *args, **kargs): + return self.data.translate(*args, **kargs) + + def upper(self, *args, **kargs): + return self.data.upper(*args, **kargs) + + def zfill(self, *args, **kargs): + return self.data.zfill(*args, **kargs) + +class HimoAsciiError(Exception): + pass diff --git a/examples/config/config.cfg b/examples/config/config.cfg new file mode 100644 index 0000000..36b0414 --- /dev/null +++ b/examples/config/config.cfg @@ -0,0 +1,29 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +[title] +foo=bar + + +[test_db] +protocol=postgresql +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing diff --git a/examples/config/connect b/examples/config/connect new file mode 100644 index 0000000..36b0414 --- /dev/null +++ b/examples/config/connect @@ -0,0 +1,29 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +[title] +foo=bar + + +[test_db] +protocol=postgresql +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing diff --git a/examples/example_01.py b/examples/example_01.py new file mode 100644 index 0000000..7915e5f --- /dev/null +++ b/examples/example_01.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +############################################################################## +# The following is for setting up the correct python path. Ignore this +# section for your project +import sys +import os.path as p +path = p.dirname(p.dirname(p.abspath(__file__))) +sys.path.append(path) +############################################################################## + +from dodai.config import Config + +def main(config): + + config.log.debug('testing one two three') + config.log.critical('testing of critical') + print config.__dict__ + + + +if __name__ == "__main__": + + config = Config() + config.options().add_quiet() + config.options().add_log_level() + config.options().parser.add_option('-c', '--crazy', dest='crazy', + default=False, help="Crazy mode") + config.set('crazy', config.options().get_options().crazy) + config.set('verbose', config.options().get_options().verbose) + + + path = p.join(p.dirname(p.abspath(__file__)), 'logs') + config.logs().set_directory(path) + config.logs().set_log_level(config.options().get_options().log_level) + log = config.logs().load(__file__) + config.logs().attach_file_handler(log, 'example.log') + config.logs().attach_screen_handler(log, 'critical') + config.set('log', log) + + + path = p.join(p.dirname(p.abspath(__file__)), 'config') + config.files().set_directory(path) + config.files().load('config.cfg') + foo = config.files().parser().get('title', 'foo') + config.set('foo', foo) + + + + + + main(config()) diff --git a/examples/example_02.py b/examples/example_02.py new file mode 100644 index 0000000..2844121 --- /dev/null +++ b/examples/example_02.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of dodai. +# +# dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Foobar 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with dodai. If not, see . + + +############################################################################## +# The following is for setting up the correct python path. Ignore this +# section for your project +import sys +import os.path as p +path = p.dirname(p.dirname(p.abspath(__file__))) +sys.path.append(path) +############################################################################## + +""" +This example requires that you have sqlalchemy installed as well as the +psycopg2 package. + +On ubuntu you can do: + apt-get install libpq-dev + easy_install psycopg2 + easy_install sqlalchemy + +""" + + +from dodai.config import Config + + +def main(config): + print config.db.__dict__ + + +if __name__ == "__main__": + + config = Config() + path = p.join(p.dirname(p.abspath(__file__)), 'config') + config.files().set_directory(path) + config.files().load('config.cfg') + config.dbs().add_config(config_parser=config.files().parser()) + db = config.dbs().load('test_db') + config.set('db', db) + main(config()) diff --git a/examples/example_03.py b/examples/example_03.py new file mode 100644 index 0000000..b94c39e --- /dev/null +++ b/examples/example_03.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of dodai. +# +# dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Foobar 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with dodai. If not, see . + + +############################################################################## +# The following is for setting up the correct python path. Ignore this +# section for your project +import sys +import os.path as p +path = p.dirname(p.dirname(p.abspath(__file__))) +sys.path.append(path) +############################################################################## + +""" +This example requires that you have sqlalchemy installed as well as the +psycopg2 package. + +On ubuntu you can do: + apt-get install libpq-dev + easy_install psycopg2 + easy_install sqlalchemy + +""" + +from dodai import Configure + + +def main(config): + print config.db.engine + print config.db.session + print config.home_directory + +if __name__ == "__main__": + + config = Configure('test') + db = config.dbs().load('test_db') + config.set('db', db) + config.set('home_directory', config.home_directory) + main(config()) diff --git a/pavement.py b/pavement.py new file mode 100644 index 0000000..ff49926 --- /dev/null +++ b/pavement.py @@ -0,0 +1,116 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +PACKAGE = 'dodai' +LICENSE='GPLv3' +VERSION = '0.3' +AUTHOR='Leonard Thomas' +AUTHOR_EMAIL='six@choushi.net' +URL='http://code.google.com/p/dodai' +DOWNLOAD_URL = 'http://code.google.com/p/dodai/downloads/list' +PY_VERSION_LOW = '2.6.0' +PY_VERSION_HIGH = '3.0.0' +PLATFORMS = ['Linux'] +SETUP_REQUIRES=['sqlalchemy'] +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Affero General Public License v3', + 'Natural Language :: English', + 'Operating System :: POSIX', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Topic :: Database', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: Libraries :: Python Modules',] +DESCRIPTION = 'Tools for writing python scripts' +LONG_DESCRIPTION = "This module is to be a foundation to your command line "\ + "python scripts. This module provides for quick access to logger, "\ + "configparser, optionparse and databases via sqlalchemy."\ + + + +import sys +import platform +import paver +import paver.setuputils +from paver.easy import options +from paver.easy import Bunch +from paver.easy import task +from paver.easy import needs +from paver.misctasks import generate_setup +from paver.misctasks import minilib +from setuptools import setup +from setuptools import find_packages + +#from paver.easy import * +#from paver.misctasks import generate_setup, minilib +#from setuptools import setup, find_packages +#import paver.setuputils +paver.setuputils.install_distutils_tasks() + +options( + setup=Bunch( + name=PACKAGE, + version=VERSION, + zip_safe=False, + description=DESCRIPTION, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + url=URL, + download_url=DOWNLOAD_URL, + packages=find_packages(), + classifiers=CLASSIFIERS, + long_description=LONG_DESCRIPTION, + license=LICENSE, + setup_requires=SETUP_REQUIRES, + install_requires=[], + platforms = PLATFORMS + +)) + + +def is_valid_version(): + if sys.version >= PY_VERSION_LOW and sys.version < PY_VERSION_HIGH: + return True + else: + return False + +def is_valid_platform(): + if platform.system() in PLATFORMS: + return True + else: + return False + +@task +def build(): + if not is_valid_version(): + raise Exception('Invalid Python version') + if not is_valid_platform(): + error='{0} not install on: {1}'.format(PACKAGE, platform.system()) + raise Exception(error) + + +@task +@needs('build', 'generate_setup', 'minilib', 'setuptools.command.sdist') +def sdist(): + """Overrides sdist to make sure that our setup.py is generated.""" + pass diff --git a/test/config/connection b/test/config/connection new file mode 100644 index 0000000..36b0414 --- /dev/null +++ b/test/config/connection @@ -0,0 +1,29 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + + +[title] +foo=bar + + +[test_db] +protocol=postgresql +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing diff --git a/test/test_config/config b/test/test_config/config new file mode 100644 index 0000000..d3b7019 --- /dev/null +++ b/test/test_config/config @@ -0,0 +1,19 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +[test_section] +foo=bar diff --git a/test/test_config/config.cfg b/test/test_config/config.cfg new file mode 100644 index 0000000..14764ae --- /dev/null +++ b/test/test_config/config.cfg @@ -0,0 +1,67 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +[testing] +foo=bar + + + +[test_db] +protocol=postgresql +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing +schema=tesuto + +[test_db_two] +protocol=postgre +protocol_extra=psycopg2 +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing + +[test_db_three] +protocol=postgresss +hostname=127.0.0.1 +username=test +password=test +port=12345 +database=testing + +[test_db_four] +protocol=postgre +hostname=127.0.0.1 +username=test +password=test +port=tea +database=testing + +[test_db_five] +protocol=postgres +hostname=127.0.0.1 +username=test +password=test +port=12345ad +database=testing + +[test_db_six] +protocol=sqlite +filename=/tmp/test diff --git a/test/test_config/test_db.py b/test/test_config/test_db.py new file mode 100644 index 0000000..ed46eae --- /dev/null +++ b/test/test_config/test_db.py @@ -0,0 +1,106 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +import os +from dodai.config.db import ConfigDb +from dodai.config.db import ConfigDbFile +from dodai.config.db import BaseConfigDb +from dodai.config.db import NotConfigParserObject +from dodai.config.db import InvalidProtocolException +from dodai.config.db import InvalidPortException +from dodai.config.db import UnknownHandlerException +from dodai.config.db import UnknownConnectionException +from dodai.config.db.sa import Sa +from dodai.config.file import ConfigFile +from dodai.db import Db + +class TestConfigDb(unittest.TestCase): + + + def setUp(self): + self.config_db = ConfigDb() + config = ConfigFile() + config.set_directory(os.path.dirname(os.path.abspath(__file__))) + config.load('config.cfg') + self.parser = config.parser() + + def test_setup(self): + obj = self.config_db._handlers['sa'][0] + self.assertTrue(obj == Sa) + + def test_register_handler(self): + self.config_db.register_handler('foo', Exception) + self.assertTrue('foo' in self.config_db._handlers.keys()) + + def test_add_config_one(self): + self.config_db.add_config(config_parser=self.parser) + self.assertTrue('test_db' in self.config_db.connections.keys()) + + def test_add_config_two(self): + self.failUnlessRaises(NotConfigParserObject, self.config_db.add_config, + config_parser='blah') + + def test_load_one(self): + self.config_db.add_config(config_parser=self.parser) + obj = self.config_db.load('test_db') + self.assertTrue(isinstance(obj, Db)) + + def test_load_two(self): + self.config_db.add_config(config_parser=self.parser) + obj = self.config_db.load('test_db') + obj = self.config_db.load('test_db') + self.assertTrue(isinstance(obj, Db)) + + def test_load_handler(self): + self.failUnlessRaises(UnknownHandlerException, + self.config_db._load_handler, 'test') + + def test_clean_protocol_one(self): + self.config_db.add_config(config_parser=self.parser) + obj = self.config_db.load('test_db_two') + self.assertTrue(isinstance(obj, Db)) + + def test_clean_protocol_one(self): + self.config_db.add_config(config_parser=self.parser) + obj = self.config_db.load('test_db_two') + self.assertTrue(isinstance(obj, Db)) + + def test_clean_protocol_two(self): + self.config_db.add_config(config_parser=self.parser) + self.failUnlessRaises(InvalidProtocolException, self.config_db.load, + 'test_db_three') + + def test_clean_port_one(self): + obj = BaseConfigDb() + data = obj._clean_port('ad') + self.assertTrue(data == None) + + def test_clean_port_two(self): + obj = BaseConfigDb() + data = obj._clean_port(None) + self.assertTrue(data == None) + + def test_clean_port_three(self): + obj = BaseConfigDb() + self.failUnlessRaises(InvalidPortException, obj._clean_port, 66666) + + + def test_file_load_one(self): + self.config_db.add_config(config_parser=self.parser) + obj = self.config_db.load('test_db_six') + self.assertTrue(isinstance(obj, Db)) diff --git a/test/test_config/test_file.py b/test/test_config/test_file.py new file mode 100644 index 0000000..1ed014e --- /dev/null +++ b/test/test_config/test_file.py @@ -0,0 +1,101 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +import os +from dodai.config.file import ConfigFile +from dodai.config.file import DirectoryDoesNotExistException +from dodai.config.file import DirectoryNotSetException +from dodai.config.file import InvalidDirectoryException +from dodai.config.file import FileDoesNotExistException +from dodai.config.file import NoFilesToLoadException +import ConfigParser + +class TestConfigFile(unittest.TestCase): + + def setUp(self): + self.obj = ConfigFile() + self.path = os.path.dirname(__file__) + self.filename = 'config' + self.filepath = os.path.join(self.path, self.filename) + next_file = os.path.join(self.path, 'config.cfg') + self.paths = [self.filepath, next_file] + + def test_file_add(self): + self.obj.add_file(self.filepath) + paths = [self.filepath] + files = self.obj.get_files() + self.assertEqual(files, paths) + + def test_double_file_add(self): + self.obj.add_file(self.filepath) + self.obj.add_file(self.filepath) + paths = [self.filepath] + files = self.obj.get_files() + self.assertEqual(files, paths) + + def test_file_does_not_exist(self): + path = os.path.join(self.path, 'foo') + self.failUnlessRaises(FileDoesNotExistException, + self.obj.add_file, path) + + def test_multiple_file_add(self): + self.obj.add_file(self.paths) + files = self.obj.get_files() + self.assertEqual(files, self.paths) + + def test_empty_parser(self): + self.failUnlessRaises(Exception, self.obj.parser) + + def test_parser(self): + self.obj.add_file(self.filepath) + parser = self.obj.parser() + self.assertTrue(isinstance(parser, ConfigParser.ConfigParser)) + + def test_parser_error(self): + self.failUnlessRaises(NoFilesToLoadException, self.obj.parser) + + def test_set_directory(self): + self.obj.set_directory(self.path) + dir = self.obj.get_directory() + self.assertEqual(dir, self.path) + + def test_invalid_directory(self): + self.failUnlessRaises(InvalidDirectoryException, + self.obj.set_directory, self.filepath) + + def test_directory_does_not_exist(self): + path = os.path.join(self.path, 'nowayjose') + self.failUnlessRaises(DirectoryDoesNotExistException, + self.obj.set_directory, path) + + def test_load(self): + self.obj.set_directory(self.path) + self.obj.load(self.filename) + check = [self.filepath] + self.assertEqual(check, self.obj.get_files()) + + def test_no_directory_set(self): + self.failUnlessRaises(DirectoryNotSetException, + self.obj.load, self.filename) + + def test_reset_parser(self): + self.obj.add_file(self.filepath) + self.obj.parser() + self.obj.add_file(self.paths) + self.obj.parser() + self.assertEqual(self.obj.files_loaded, self.obj.get_files()) diff --git a/test/test_config/test_init.py b/test/test_config/test_init.py new file mode 100644 index 0000000..92b44c6 --- /dev/null +++ b/test/test_config/test_init.py @@ -0,0 +1,62 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +from dodai.config import Config +from dodai.config.option import ConfigOption +from dodai.config.file import ConfigFile +from dodai.config.log import ConfigLog +from dodai.config import ConfigResults +from dodai.config.db import ConfigDb + +class TestConfig(unittest.TestCase): + + def setUp(self): + self.obj = Config() + + def test_set_one(self): + self.obj.set('foo', 'bar') + self.assertTrue('foo' in self.obj.vars.keys()) + + def test_set_two(self): + self.obj.set('foo', 'bar') + self.assertTrue('bar' == self.obj.vars['foo']) + + def test_options(self): + obj = self.obj.options() + self.assertTrue(isinstance(obj, ConfigOption)) + + def test_files(self): + obj = self.obj.files() + self.assertTrue(isinstance(obj, ConfigFile)) + + def test_logs(self): + obj = self.obj.logs() + self.assertTrue(isinstance(obj, ConfigLog)) + + def test_call_one(self): + obj = self.obj() + self.assertTrue(isinstance(obj, ConfigResults)) + + def test_call_two(self): + self.obj.set('foo', 'bar') + obj = self.obj() + self.assertTrue(obj.foo == 'bar') + + def test_db_one(self): + obj = self.obj.dbs() + self.assertTrue(isinstance(obj, ConfigDb)) diff --git a/test/test_config/test_log.py b/test/test_config/test_log.py new file mode 100644 index 0000000..d3c46bc --- /dev/null +++ b/test/test_config/test_log.py @@ -0,0 +1,147 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +import os +import logging +from logging import Logger +from dodai.config.log import ConfigLog +from dodai.config.log import NoDirectoryExistException +from dodai.config.log import DirectoryNotSetException +from dodai.config.log import InvalidLevelException +from logging.handlers import RotatingFileHandler +from logging import StreamHandler + +class TestConfigLog(unittest.TestCase): + + + def setUp(self): + self.obj = ConfigLog() + self.path = os.path.dirname(__file__) + self.filename = 'test_log_file' + self.filepath = os.path.join(self.path, self.filename) + + def tearDown(self): + if os.path.isfile(self.filepath): + os.remove(self.filepath) + + def test_set_log_level_one(self): + self.obj.set_log_level(logging.INFO) + self.assertTrue(self.obj.log_level == logging.INFO) + + def test_set_log_level_two(self): + self.obj.set_log_level('wArnIng') + self.assertTrue(self.obj.log_level == logging.WARNING) + + def test_set_log_level_three(self): + default = self.obj.log_level + self.obj.set_log_level('i08w9umY3ngas') + self.assertTrue(self.obj.log_level == default) + + def test_fetch_log_level_one(self): + level = self.obj._fetch_log_level(logging.DEBUG) + self.assertTrue(level == logging.DEBUG) + + def test_fetch_log_level_two(self): + level = self.obj._fetch_log_level('ErRoR') + self.assertTrue(level == logging.ERROR) + + def test_fetch_log_level_three(self): + self.failUnlessRaises(InvalidLevelException, + self.obj._fetch_log_level, + 'ba_ri08w9as') + + def test_get_file_message_format(self): + default = self.obj.LEVELS[logging.DEBUG][1] + out = self.obj.get_file_message_format(logging.DEBUG) + self.assertTrue(default == out) + + def test_get_screen_message_format(self): + default = self.obj.LEVELS[logging.WARNING][2] + out = self.obj.get_screen_message_format(logging.WARNING) + self.assertTrue(default == out) + + def test_set_directory_one(self): + self.obj.set_directory(self.path) + self.assertTrue(self.obj.directory == self.path) + + def test_set_directory_two(self): + self.failUnlessRaises(NoDirectoryExistException, + self.obj.set_directory, + __file__) + + def test_load_one(self): + log = self.obj.load('test') + self.assertTrue(isinstance(log, Logger)) + + def test_build_filepath_one(self): + path = self.obj._build_filepath(self.filepath) + self.assertTrue(self.filepath == path) + + def test_build_filepath_two(self): + self.obj.set_directory(self.path) + path = self.obj._build_filepath(self.filename) + self.assertTrue(self.filepath == path) + + def test_build_filepath_three(self): + self.failUnlessRaises(NoDirectoryExistException, + self.obj._build_filepath, + os.path.join(__file__, 'blah')) + + def test_build_filepath_four(self): + self.failUnlessRaises(DirectoryNotSetException, + self.obj._build_filepath, + self.filename) + + def test_attach_file_handler(self): + self.obj.set_log_level(logging.WARNING) + self.obj.set_directory(self.path) + log = self.obj.load('test') + self.obj.attach_file_handler(log, self.filename) + handlers = log.handlers + has_stream = False + for handle in handlers: + print handle + if isinstance(handle, RotatingFileHandler): + has_stream = True + self.assertTrue(has_stream, 'RotatingFileHandler is missing') + + def test_attach_screen_handler_one(self): + self.obj.set_log_level(logging.DEBUG) + self.obj.set_directory(self.path) + log = self.obj.load('testing') + self.obj.attach_screen_handler(log, logging.CRITICAL) + handlers = log.handlers + has_stream = False + for handle in handlers: + print handle + if isinstance(handle, StreamHandler): + has_stream = True + self.assertTrue(has_stream, 'StreamHandler is missing') + + def test_attach_screen_handler_two(self): + self.obj.set_log_level(logging.DEBUG) + self.obj.set_directory(self.path) + log = self.obj.load('testing') + self.obj.attach_screen_handler(log) + handlers = log.handlers + has_stream = False + for handle in handlers: + print handle + if isinstance(handle, StreamHandler): + has_stream = True + self.assertTrue(has_stream, 'StreamHandler is missing') diff --git a/test/test_config/test_option.py b/test/test_config/test_option.py new file mode 100644 index 0000000..3fc9e6f --- /dev/null +++ b/test/test_config/test_option.py @@ -0,0 +1,53 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +from dodai.config.option import ConfigOption +from optparse import OptionParser + +class TestConfigOption(unittest.TestCase): + + def setUp(self): + self.obj = ConfigOption() + + def test_parser(self): + self.assertTrue(isinstance(self.obj.parser, OptionParser)) + + def test_add_quiet(self): + self.obj.add_quiet() + self.assertTrue(self.obj.parser.has_option('-q')) + + def test_add_verbose(self): + self.obj.add_verbose() + self.assertTrue(self.obj.parser.has_option('-v')) + + def test_add_log_level(self): + self.obj.add_log_level('critical') + self.assertTrue(self.obj.parser.has_option('-l')) + + def test_add_setup(self): + self.obj.add_setup() + self.assertTrue(self.obj.parser.has_option('--setup')) + + def test_get_args(self): + args = self.obj.get_args() + self.assertTrue(args == []) + + def test_get_options(self): + self.obj.add_quiet() + options = self.obj.get_options() + self.assertTrue(options.verbose == True) diff --git a/test/test_config/test_sections.py b/test/test_config/test_sections.py new file mode 100644 index 0000000..2aba3c7 --- /dev/null +++ b/test/test_config/test_sections.py @@ -0,0 +1,114 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import sys +import os +import ConfigParser +import unittest +path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) +sys.path.append(path) +from dodai.tools.himo import Himo +from dodai.config.sections import ConfigSections + + +class TestSections(unittest.TestCase): + + def setUp(self): + path = os.path.dirname(__file__) + filepath = os.path.join(path, 'config.cfg') + self.parser = ConfigParser.ConfigParser() + self.parser.readfp(open(filepath)) + self.sections = ConfigSections(Himo) + + def test_call(self): + self.sections(self.parser) + count = len(self.parser.sections()) + self.assertEqual(count, len(self.sections)) + + def test_iterator(self): + result = True + for section in self.sections: + if not section.get_title() in self.parser.sections(): + result = False + self.assertTrue(result==True) + + def test_get_section(self): + self.sections(self.parser) + self.sections(self.parser) + check = self.sections.get_section('test_db') + self.assertEqual(check.get_title(), 'test_db') + + def test_build_string_object(self): + sections = ConfigSections() + sections(self.parser) + count = len(self.parser.sections()) + self.assertEqual(count, len(sections)) + + def test_getitem(self): + self.sections(self.parser) + title = self.sections['test_db'].get_title() + self.assertEqual(title, 'test_db') + + def test_getattr(self): + self.sections(self.parser) + title = self.sections.test_db.get_title() + self.assertEqual(title, 'test_db') + + def test_get_keys(self): + self.sections(self.parser) + keys = self.sections.keys() + self.assertTrue(len(keys)) + + def test_section_object_one(self): + self.sections(self.parser) + keys = self.sections.test_db.keys() + self.assertTrue(len(keys)) + + def test_section_object_two(self): + self.sections(self.parser) + keys = self.sections.test_db.___options___.keys() + self.assertTrue(len(keys)) + + def test_section_object_three(self): + self.sections(self.parser) + self.sections.test_db.___blah___ = 'test' + val = self.sections.test_db.__getattr__('___blah___') + self.assertTrue(val == 'test') + + def test_section_object_four(self): + self.sections(self.parser) + self.sections.test_db.foo = 'bar' + val = self.sections.test_db.__getattr__('foo') + self.assertTrue(val == 'bar') + + def test_section_object_five(self): + self.sections(self.parser) + keys = [] + for key in self.sections.test_db: + keys.append(key) + self.assertTrue(keys) + + def test_section_object_six(self): + self.sections(self.parser) + self.sections.test_db.foo = 'bar' + val = self.sections.test_db['foo'] + self.assertTrue(val == 'bar') + + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_dodai.py b/test/test_dodai.py new file mode 100644 index 0000000..a521a38 --- /dev/null +++ b/test/test_dodai.py @@ -0,0 +1,38 @@ +# Copyright (C) 2010 Leonard Thomas +# +# This file is part of Dodai. +# +# Dodai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Dodai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Dodai. If not, see . + +import unittest +import os +from dodai import Configure + +class TestConfigure(unittest.TestCase): + + def setUp(self): + path = os.path.dirname(os.path.abspath(__file__)) + path = os.path.join(path, 'config') + filename = os.path.join(path, 'connection') + self.filename = filename + self.obj = Configure('test', config_files=[filename]) + + def test_results(self): + files = self.obj.config_files + self.assertTrue(len(files) == 1) + + def test_add_files(self): + self.obj._add_files(self.filename) + files = self.obj.config_files + self.assertTrue(len(files) == 1) -- cgit v1.2.3