Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 67 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
67
Dung lượng
547,27 KB
Nội dung
CHAPTER 18 ■ PACKAGING YOUR PROGRAMS 385 You should now see output like the following: running build running build_py creating build creating build/lib copying hello.py -> build/lib Distutils has created a subdirectory called build, with yet another subdirectory named lib, and placed a copy of hello.py in build/lib. The build subdirectory is a sort of working area where Distutils assembles a package (and compiles extension libraries, for example). You don’t really need to run the build command when installing, because it will be run automati- cally, if needed, when you run the install command. ■Note In this example, the install command will copy the hello.py module to some system-specific directory in your PYTHONPATH. This should not pose a risk, but if you don’t want to clutter your system, you might want to remove it afterward. Make a note of the specific location where it is placed, as output by setup.py. You could also use the -n switch to do a dry run. At the time of writing, there is no standard uninstall command (although you can find custom uninstallation implementations online), so you’ll need to uninstall the module by hand. Speaking of which . . . let’s try to install the module: python setup.py install Now you should see something like the following: running install running build running build_py running install_lib copying build/lib/hello.py -> /path/to/python/lib/python2.5/site-packages byte-compiling /path/to/python/lib/python2.5/site-packages/hello.py to hello.pyc 386 CHAPTER 18 ■ PACKAGING YOUR PROGRAMS ■Note If you’re running a version of Python that you didn’t install yourself, and don’t have the proper priv- ileges, you may not be allowed to install the module as shown, because you don’t have write permissions to the correct directory. This is the standard mechanism used to install Python modules, packages, and extensions. All you need to do is provide the little setup script. The sample script uses only the Distutils directive py_modules. If you want to install entire packages, you can use the directive packages in an equivalent manner (just list the package names). You can set many other options (some of which are covered in the section “Compiling Extensions,” later in this chapter). You can also create configuration files for Distutils to set var- ious properties (see the section “Distutils Configuration Files” in “Installing Python Modules,” http://python.org/doc/inst/config-syntax.html). The various ways of providing options (command-line switches, keyword arguments to setup, and Distutils configuration files) let you specify such things as what to install and where to install it. And these options can be used for more than one thing. The following section shows you how to wrap the modules you specified for installation as an archive file, ready for distribution. Wrapping Things Up Once you’ve written a setup.py script that will let the user install your modules, you can use it yourself to build an archive file, a Windows installer, or an RPM package. Building an Archive File You do this with the sdist (for “source distribution”) command: python setup.py sdist If you run this, you will probably get quite a bit of output, including some warnings. The warnings I get include a complaint about a missing author_email option, a missing MANIFEST.in file, and a missing README file. You can safely ignore all of these (although feel free to add an author_email option to your setup.py script, similar to the author option, a README or README.txt text file, and an empty file called MANIFEST.in in the current directory). After the warnings you should see output like the following: writing manifest file 'MANIFEST' creating Hello-1.0 making hard links in Hello-1.0 hard linking hello.py -> Hello-1.0 hard linking setup.py -> Hello-1.0 tar -cf dist/Hello-1.0.tar Hello-1.0 gzip -f9 dist/Hello-1.0.tar removing 'Hello-1.0' (and everything under it) CHAPTER 18 ■ PACKAGING YOUR PROGRAMS 387 As you can see, when you create a source distribution, a file called MANIFEST is created. This file contains a list of all your files. The MANIFEST.in file is a template for the manifest, and it is used when figuring out what to install. You can include lines like the following to specify files that you want to have included, if Distutils hasn’t figured it out by itself, using your setup.py script (and default includes, such as README): include somedirectory/somefile.txt include somedirectory/* ■Note If you’ve run the sdist command before, and you have a file called MANIFEST already, you will see the word reading instead of writing at the beginning. If you’ve restructured your package and want to repackage it, deleting the MANIFEST file can be a good idea, in order to start afresh. Now, in addition to the build subdirectory, you should have one called dist. Inside it, you will find a gzip’ed tar archive called Hello-1.0.tar.gz. This can now be distributed to others, and they can unpack it and install it using the included setup.py script. If you don’t want a .tar.gz file, plenty of other distribution formats are available, and you can set them all through the command-line switch formats. (As the plural name indicates, you can supply more than one format, separated by commas, to create more archive files in one go.) The format names available in Python 2.5 (accessible through the help-formats switch to the sdist command) are bztar (for bzip2’ed tar files), gztar (the default, for gzip’ed tar files), tar (for uncompressed tar files), zip (for ZIP files), and ztar (for compressed tar files, using the UNIX command compress). Creating a Windows Installer or an RPM Package Using the command bdist, you can create simple Windows installers and Linux RPM files. (You normally use this to create binary distributions, where extensions have been compiled for a particular architecture. See the following section for information about compiling exten- sions.) The formats available for bdist (in addition to the ones available for sdist) are rpm (for RPM packages) and wininst (for Windows executable installer). One interesting twist is that you can, in fact, build Windows installers for your package in non-Windows systems, provided that you don’t have any extensions you need to compile. If you have access to both, say, a Linux machine and a Windows box, you could try running the following on a Linux machine: python setup.py bdist formats=wininst Then (after ignoring a few warnings about compiler settings) copy the file dist/ Hello-1.0.win32.exe to your Windows machine and run it. You should be presented with a rudimentary installer wizard. (You can cancel the process before actually installing the module.) 388 CHAPTER 18 ■ PACKAGING YOUR PROGRAMS Compiling Extensions In Chapter 17, you saw how to write extensions for Python. You may agree that compiling these extensions could be a bit cumbersome at times. Luckily, you can use Distutils for this as well. You may want to refer back to Chapter 17 for the source code to the program palindrome (in Listing 17-6). Assuming that you have the source file palindrome2.c in the current (empty) directory, the following setup.py script could be used to compile (and install) it: from distutils.core import setup, Extension setup(name='palindrome', version='1.0', ext_modules = [ Extension('palindrome', ['palindrome2.c']) ]) If you run the install command with this setup.py script, the palindrome extension mod- ule should be compiled automatically before it is installed. As you can see, instead of specifying a list of module names, you give the ext_modules argument a list of Extension instances. The constructor takes a name and a list of related files; this is where you would specify header (.h) files, for example. If you would rather just compile the extension in place (resulting in a file called palindrome.so in the current directory for most UNIX systems), you can use the following command: python setup.py build_ext inplace USING A REAL INSTALLER The installer you get with the wininst format in Distutils is very basic. As with normal Distutils installation, it will not let you uninstall your packages, for example. This may be acceptable in some situations, but some- times you may want a more professional look, especially if you’re creating an executable using py2exe (as described in this chapter). In this case, you might want to consider using some standard installer such as Inno Setup (http://jrsoftware.org/isinfo.php), which works very well with executables created with py2exe. This type of installer will install your program in a more normal Windows fashion and give you func- tionality such as the ability to uninstall the program. A more Python-centric (but, at present, unmaintained) option is the McMillan installer (a web search should give you an updated download location), which can also work as an alternative to py2exe when building executable programs. Other options include InstallShield (http://installshield.com), Wise installer (http://wise.com), Installer VISE (http://www.mindvision.com), Nullsoft Scriptable Install System (http://nsis.sf.net), Youseful Windows Installer (http://youseful.com), and Ghost Installer (http://ethalone.com). A web search will probably turn up several other solutions. For more information about Windows installer technology, see Phil Wilson’s The Definitive Guide to Windows Installer (Apress, 2004). CHAPTER 18 ■ PACKAGING YOUR PROGRAMS 389 Now we get to a real juicy bit. If you have SWIG installed (see Chapter 17), you can have Distutils use it directly! Take a look at the source for the original palindrome.c (without all the wrapping code) in Listing 17-3. It’s certainly much simpler than the wrapped-up version. Being able to compile it directly as a Python extension, having Distutils use SWIG for you, can be very convenient. It’s all very simple, really—you just add the name of the interface (.i) file (see Listing 17-5) to the list of files in the Extension instance: from distutils.core import setup, Extension setup(name='palindrome', version='1.0', ext_modules = [ Extension('palindrome', ['palindrome.c', 'palindrome.i']) ]) If you run this script using the same command as before (build_ext, possibly with the inplace switch), you should end up with a palindrome.so file again, but this time without needing to write all the wrapper code yourself. Creating Executable Programs with py2exe The py2exe extension to Distutils (available from http://www.py2exe.org) allows you to build executable Windows programs (.exe files), which can be useful if you don’t want to burden your users with having to install a Python interpreter separately. ■Tip After creating your executable program, you may want to use an installer, such as Inno Setup ( http://jrsoftware.org/isinfo.php), to distribute the executable program and the accompanying files created by py2exe. See the “Using a Real Installer” sidebar. The py2exe package can be used to create executables with GUIs (such as wx, as described in Chapter 12). Let’s use a very simple example here (it uses the raw_input trick first discussed in the section “What About Double-Clicking?” in Chapter 1): print 'Hello, world!' raw_input('Press <enter>') Again, starting in an empty directory containing only this file, called hello.py, create a setup.py file like this: from distutils.core import setup import py2exe setup(console=['hello.py']) 390 CHAPTER 18 ■ PACKAGING YOUR PROGRAMS You can run this script like this: python setup.py py2exe This will create a console application (called hello.exe) along with a couple of other files in the dist subdirectory. You can either run it from the command line or double-click it. For more information about how py2exe works, and how you can use it in more advanced ways, visit the py2exe web site (http://www.py2exe.org). ■Tip If you’re using Mac OS, you might want to check out Bob Ippolito’s py2app (http://undefined.org/ python/py2app.html). A Quick Summary Finally, you now know how to create shiny, professional-looking software with fancy GUI installers—or how to automate the generation of those precious .tar.gz files. Here is a sum- mary of the specific concepts covered: Distutils: The Distutils toolkit lets you write installer scripts, conventionally called setup.py. With these scripts, you can install modules, packages, and extensions. You can also build distributable archives and simple Windows installers. Distutils commands: You can run your setup.py script with several commands, such as build, build_ext, install, sdist, and bdist. Installers: Many installer generators are available. Using an installer to install your Python program makes the process easier for your users. Compiling extensions: You can use Distutils to have your C extensions compiled automat- ically, with Distutils automatically locating your Python installation and figuring out which compiler to use. You can even have it run SWIG automatically. Executable binaries: The py2exe extension to Distutils can be used to create executable binaries from your Python programs. Along with a couple of extra files (which can be LETTING THE WORLD KNOW You have a choice of many places to announce your new software, such as Freshmeat (http://freshmeat. net). There is, however, a standard, centralized index of Python packages called, fittingly, the Python Package Index, or simply PyPI. Visit the PyPI web site (http://pypi.python.org) to look for new packages or new versions of old packages, or to publish your own packages. In addition to the packages themselves, you can register a lot of useful metadata (possibly with the aid of Distutils or its relation setuptools), such as author, license, platform, categories, and descriptive key- words. The register command in Distutils will do most of the work for you. CHAPTER 18 ■ PACKAGING YOUR PROGRAMS 391 conveniently installed with an installer), these .exe files can be run without installing a Python interpreter separately. New Functions in This Chapter What Now? That’s it for the technical stuff—sort of. In the next chapter, you get some programming meth- odology and philosophy, and then come the projects. Enjoy! Function Description distutils.core.setup( ) Configures Distutils with keyword arguments in your setup.py script 393 ■ ■ ■ CHAPTER 19 Playful Programming At this point, you should have a clearer picture of how Python works than when you started. Now the rubber hits the road, so to speak, and in the next ten chapters you put your newfound skills to work. Each chapter contains a single do-it-yourself project with a lot of room for exper- imentation, while at the same time giving you the necessary tools to implement a solution. In this chapter, I give you some general guidelines for programming in Python. Why Playful? I think one of the strengths of Python is that it makes programming fun—for me, anyway. It’s much easier to be productive when you’re having fun; and one of the fun things about Python is that it allows you to be very productive. It’s a positive feedback loop, and you get far too few of those in life. The expression Playful Programming is one I invented as a less extreme version of Extreme Programming, or XP. 1 I like many of the ideas of the XP movement but have been too lazy to commit completely to their principles. Instead, I’ve picked up a few things, and combined them with what I feel is a natural way of developing programs in Python. The Jujitsu of Programming You have perhaps heard of jujitsu? It’s a Japanese martial art, which, like its descendants judo and aikido, 2 focuses on flexibility of response, or “bending instead of breaking.” Instead of trying to impose your preplanned moves on an opponent, you go with the flow, using your opponent’s movements against him. This way (in theory), you can beat an opponent who is bigger, meaner, and stronger than you. How does this apply to programming? The key is the syllable “ju,” which may be (very roughly) translated as flexibility. When you run into trouble while programming (as you invari- ably will), instead of trying to cling stiffly to your initial designs and ideas, be flexible. Roll with the punches. Be prepared to change and adapt. Don’t treat unforeseen events as frustrating 1. Extreme Programming is an approach to software development that, arguably, has been in use by pro- grammers for years, but that was first named and documented by Kent Beck. For more information, see http://www.extremeprogramming.org. 2. Or, for that matter, its Chinese relatives, such as taijiquan or baguazhang. 394 CHAPTER 19 ■ PLAYFUL PROGRAMMING interruptions; treat them as stimulating starting points for creative exploration of new options and possibilities. The point is that when you sit down and plan how your program should be, you don’t have any real experience with that specific program. How could you? After all, it doesn’t exist yet. By working on the implementation, you gradually learn new things that could have been useful when you did the original design. Instead of ignoring these lessons you pick up along the way, you should use them to redesign (or refactor) your software. I’m not saying that you should just start hacking away with no idea of where you are headed, but that you should prepare for change, and accept that your initial design will need to be revised. It’s like the old writer’s say- ing: “Writing is rewriting.” This practice of flexibility has many aspects; here I’ll touch upon two of them: Prototyping: One of the nice things about Python is that you can write programs quickly. Writing a prototype program is an excellent way to learn more about your problem. Configuration: Flexibility comes in many forms. The purpose of configuration is to make it easy to change certain parts of your program, both for you and your users. A third aspect, automated testing, is absolutely essential if you want to be able to change your program easily. With tests in place, you can be sure that your program still works after introducing a modification. Prototyping and configuration are discussed in the following sec- tions. For information about testing, see Chapter 16. Prototyping In general, if you wonder how something works in Python, just try it. You don’t need to do extensive preprocessing, such as compiling or linking, which is necessary in many other lan- guages. You can just run your code directly. And not only that, you can run it piecemeal in the interactive interpreter, prodding at every corner until you thoroughly understand its behavior. This kind of exploration doesn’t cover only language features and built-in functions. Sure, it’s useful to be able to find out exactly how, say, the iter function works, but even more impor- tant is the ability to easily create a prototype of the program you are about to write, just to see how that works. ■Note In this context, the word prototype means a tentative implementation, a mock-up that implements the main functionality of the final program, but which may need to be completely rewritten at some later stage—or not. Quite often, what started out as a prototype can be turned into a working program. After you have put some thought into the structure of your program (such as which classes and functions you need), I suggest implementing a simple version of it, possibly with very lim- ited functionality. You’ll quickly notice how much easier the process becomes when you have a running program to play with. You can add features, change things you don’t like, and so on. You can really see how it works, instead of just thinking about it or drawing diagrams on paper. CHAPTER 19 ■ PLAYFUL PROGRAMMING 395 You can use prototyping in any programming language, but the strength of Python is that writing a mock-up is a very small investment, so you’re not committed to using it. If you find that your design wasn’t as clever as it could have been, you can simply toss out your prototype and start from scratch. The process might take a few hours, or a day or two. If you were programming in C++, for example, much more work would probably be involved in getting something up and running, and discarding it would be a major decision. By committing to one version, you lose flexibility; you get locked in by early decisions that may prove wrong in light of the real-world experience you get from actually implementing it. In the projects that follow this chapter, I consistently use prototyping instead of detailed analysis and design up front. Every project is divided into two implementations. The first is a fum- bling experiment in which I’ve thrown together a program that solves the problem (or possibly only a part of the problem) in order to learn about the components needed and what’s required of a good solution. The greatest lesson will probably be seeing all the flaws of the program in action. By building on this newfound knowledge, I take another, hopefully more informed, whack at it. Of course, you should feel free to revise the code, or even start afresh a third time. Usually, starting from scratch doesn’t take as much time as you might think. If you have already thought through the practicalities of the program, the typing shouldn’t take too long. THE CASE AGAINST REWRITING Although I’m advocating the use of prototypes here, there is reason to be a bit cautious about restarting your project from scratch at any point, especially if you’ve invested some time and effort into the prototype. It is probably better to refactor and modify that prototype into a more functional system, for several reasons. One common problem that can occur is “second system syndrome.” This is the tendency to try to make the second version so clever or perfect that it’s never finished. The “continual rewriting syndrome,” quite prevalent in fiction writing, is the tendency to keep fiddling with your program, perhaps starting from scratch again and again. At some point, leaving well enough alone may be the best strategy—just get something that works. Then there is “code fatigue.” You grow tired of your code. It seems ugly and clunky to you after you’ve worked with it for a long time. Sadly, one of the reasons it may seem hacky and clunky is that it has grown to accommodate a range of special cases, and to incorporate several forms of error handling and the like. These are features you would need to reintroduce in a new version anyway, and they have probably cost you quite a bit of effort (not the least in the form of debugging) to implement in the first place. In other words, if you think your prototype could be turned into a workable system, by all means, keep hacking at it, rather than restarting. In the project chapters that follow, I have separated the development cleanly into two versions: the prototype and the final program. This is partly for clarity and partly to highlight the experience and insight one can get by writing the first version of a piece of software. In the real world, I might very well have started with the prototype and “refactored myself” in the direction of the final system. For more on the horrors of restarting from scratch, take a look at Joel Spolsky’s article “Things You Should Never Do, Part I” (found on his web site, http://joelonsoftware.com). According to Spolsky, rewriting the code from scratch is the single worst strategic mistake that any software company can make. [...]... of the prototype, You write the prototype to find flaws in your original ideas and to learn more about how to write a program that solves your problem ■Tip If you can, it’s probably a good idea to modify your original program incrementally rather than beginning from scratch In the interest of clarity, I give you two completely separate versions of the program here Useful Tools Consider what tools might... quickly grow into a mess Even more important, it would be very difficult to make it output anything other than HTML; and one of the goals of this project is to make it easy to add other output formats Let’s assume you want to refactor your program and structure it a bit differently Second Implementation So, what did you learn from this first implementation? To make it more extensible, you need to make your... when I show you how to deal with the parser The Parser We’ve come to the heart of the application: the Parser class It uses a handler and a set of rules and filters to transform a plain-text file into a marked-up file—in this specific case, an HTML file Which methods does it need? It needs a constructor to set things up, a method to add rules, a method to add filters, and a method to parse a given file... of clinging to your initial ideas, you should be willing to and even prepared to revise and change every aspect of your program as you gain insight into the problem at hand Prototyping: One important technique for learning about a problem and possible implementations is to write a simple version of your program to see how it works In Python, this is so easy that you can write several prototypes in the... does perform some important tasks It divides the text into blocks that can be handled separately, and it applies a filter (consisting of a call to re.sub) to each block in turn This seems like a good approach to use in your final program Now what would happen if you tried to extend this prototype? You would probably add checks inside the for loop to see whether the block was a heading, a list item, or... starting to write your prototype, let’s define some goals: • The input shouldn’t be required to contain artificial codes or tags • You should be able to deal with both different blocks, such as headings, paragraphs, and list items, and in-line text, such as emphasized text or URLs • Although this implementation deals with HTML, it should be easy to extend it to other markup languages You may not be able to. .. should be able to detect the applicable block type and to format it appropriately • Filters: Use filters to wrap up some regular expressions to deal with in-line elements • Handlers: The parser uses handlers to generate output Each handler can produce a different kind of markup Although this isn’t a very detailed design, at least it gives you some ideas about how to divide your code into smaller parts... by asterisks, which I’ll discuss shortly.) But why go to such lengths? Why not just use r'\1', as in the simple version? Because then you would be committed to using the em tag, but you want the handler to be able to decide which markup to use If your handler were a (hypothetical) LaTeXRenderer, for example, you might get another result altogether: >> re.sub(r'\*(.+?)\*', handler.sub('emphasis'),... usually better to have an automated test suite than to check your test results manually (Do you see any way of automating this test?) First Implementation One of the first things you need to do is split the text into paragraphs It’s obvious from Listing 20-1 that the paragraphs are separated by one or more empty lines A better word than paragraph might be block, because this name can apply to headlines... (http://wwspam.fu/whatisspam) - How do they make it? (http://wwspam.fu/howtomakeit) - Why should I eat it? (http://wwspam.fu/whyeatit) How to get in touch with us You can get in touch with us in *many* ways: By phone (555-1234), by email (wwspam@wwspam.fu) or by visiting our customer feedback page (http://wwspam.fu/feedback) 405 406 CHAPTER 20 ■ PROJECT 1: INSTANT MARKUP To test your implementation, just use this document . and effort into the prototype. It is probably better to refactor and modify that prototype into a more functional system, for several reasons. One common problem that can occur is second system. useful to be able to find out exactly how, say, the iter function works, but even more impor- tant is the ability to easily create a prototype of the program you are about to write, just to see. may want to refer back to Chapter 17 for the source code to the program palindrome (in Listing 17- 6). Assuming that you have the source file palindrome2.c in the current (empty) directory, the