Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Extreme Testing, Integration and Sandwiches with Python Andy Robinson CEO / Chief Architect, ReportLab Inc. BCS OOPS 3 July 2002 Part 1 Introduction What is Python? • • • • • • High level, object oriented scripting language Open Source Cross-platform - based on Ansi C or Java Cross-platform GUI and DB libraries very easy to read very dynamic What is used for? • • • • • Friendly alternative to Perl Quick and dirty scripting CGI scripts Large scale application development Integrating other systems Python as an Integration Tool • • • • • • Good with files Dynamic or static linking to C-based languages Great COM support CORBA etc. Great networking libraries Great Java integration "Low-threat" uses in the corporate world ...or how to get the boss to say "Yes" • • • • • Adding a macro language to applications Rapid Prototyping of object models and algorithms Building test harnesses for other systems Data Cleaning and Transformation Python as Glue Getting Popular.... • • • 500,000+ users 15+ books Used at NASA, Yahoo, Red Hat, Infoseek, Industrial Light and Magic, IBM, Fidelity Investments and many more Part 2 - The Python Environment Where to get it - http://www.python.org/ What you get Windows distribution as an example (CD available today) • Python 2.0 - command line program • Full documentation • Windows extensions - GUI and COM support Many competing IDEs now available. Demo time... Python core on Windows • • • python20.dll - 652kb, the language, exports almost everything python.exe - 5kb console mode program pythonw.exe - 21kb non-console mode program - avoids ugly black DOS boxes when you don't want standard input/output File Types • • • .py - python source (.java) .pyc - byte-compiled, rebuilt on demand (.class) .pyd - DLL with a Python-specific entry point Part 3 A Crash Course in Python Hello World PythonWin 2.1.3 (#35, Apr 8 2002, 17:47:50) [MSC 32 bit (Intel)] on win32. Portions Copyright 1994-2001 Mark Hammond ([email protected]) see 'Help/About PythonWin' for further copyright information. >>> print 'hello world' hello world Contrast that to C++ or Java Numbers, Strings and Variables >>> 2+2 4 You can assign to variables with '=' : >>> >>> 3 >>> >>> 6 >>> x=3 x y=x*2 y Python supports all standard arithmetical operators : >>> 22/7 3 >>> 22.0/7.0 3.1428571428571428 >>> Complex numbers are supported using the letter j : >>> (3 + 1j) * (3 -1j) (10+0j) >>> Strings can be wrapped in double or single quotes: >>> greeting = 'hello' >>> epithet = "stranger" They can be concatenated (using '+') and repeated (using '*'): >>> greeting + ", " + epithet 'hello, stranger' >>> "spam" * 10 'spamspamspamspamspamspamspamspamspamspam' >>> Lists Lists are wrapped in square brackets. They can contain any Python objects. >>> mylunch = ['spam', 'eggs', 'guiness', 'raspberries', 'wafer-thin mint'] >>> mylunch[0] 'spam' >>> mylunch[1:3], mylunch[-1] (['eggs', 'guiness'], 'wafer-thin mint') >>> Tuples Parentheses indicate a tuple. Tuples are similar to lists but can't be modified once created. >>> mylunch[2] = 'tea' >>> mylunch ['spam', 'eggs', 'tea', 'raspberries', 'wafer-thin mint'] >>> meal_deal = ('burger', 'fries', 'coke') # a tuple >>> meal_deal[1] = 'onion rings' Traceback (most recent call last): File "<interactive input>", line 1, in ? TypeError: object doesn't support item assignment >>> Control Structures The For Loop The for loop operates over lists not numbers: >>> for item in mylunch: ... print item ... spam eggs tea raspberries wafer-thin mint >>> The While Loop >>> x = 2 >>> while x < 50: ... x = x * 2 ... print x ... 4 8 16 32 64 >>> If and Else The else clause is optional: >>> if 'chocolate' in mylunch: ... print "that's not allowed" ... else: ... print "enjoy your meal" ... enjoy your meal >>> Elif You can also have a number of intermediate elif (else-if) clauses. Something like switch or case in other languages. >>> >>> ... ... ... ... ... ... ... ... >>> salary = 20000 if salary < 4000: tax_rate = 0 elif salary < 29000: tax_rate = 0.25 elif salary < 100000: tax_rate = 0.4 else: emigrate() # that's a function call Functions >>> def double(x): ... return x * 2 ... >>> double(2) 4 >>> def first_and_last(aList): >>> def first_and_last(aList): ... return(aList[0], aList[-1]) ... >>> first_and_last(range(5)) (0, 4) >>> def sayHello(): ... print 'hello' ... >>> sayHello() hello >>> Functions with default arguments >>> def makeCoffee(size, milk=None, sugar=None): ... order = 'one ' + size + ' coffee' ... if milk and sugar: ... order = order + ' with milk and sugar' ... elif milk: ... order = order + ' with milk' ... elif sugar: ... order = order + ' with sugar' ... else: ... pass # pass means 'do nothing' ... return order ... >>> makeCoffee('large') 'one large coffee' >>> makeCoffee('large', 1) 'one large coffee with milk' >>> makeCoffee('large', milk=0, sugar=1) 'one large coffee with sugar' >>> Dictionaries Dictionaries are based on a hash table. Lookup time is almost constant. Enclosed by braces. Keys and values separated by a colon. >>> fur_colours = {} >>> fur_colours['Tinky-Winky'] = 'purple' >>> fur_colours['Dipsy'] = 'green' >>> fur_colours['LaLa'] = 'yellow' >>> fur_colours {'Tinky-Winky': 'purple', 'Dipsy': 'green', 'LaLa': 'yellow'} >>> fur_colours['LaLa'] 'yellow' Dictionary Methods Dictionaries support some useful methods of searching: by keys, values and whether or not a certain key is present. >>> fur_colours.keys() ['Tinky-Winky', 'Dipsy', 'LaLa'] >>> fur_colours.values() ['purple', 'green', 'yellow'] >>> fur_colours.items() # converts to a list of tuples [('Tinky-Winky', 'purple'), ('Dipsy', 'green'), ('LaLa', 'yellow')] >>> len(fur_colours) 3 >>> fur_colours.has_key('Po') 0 >>> Modules Python code is organised into modules. Some are built into Python, others are stored in external files. >>> import math >>> math.sin(math.pi/2) 1.0 >>> >>> from string import split, join >>> split('We are the knights who say Ni') ['We', 'are', 'the', 'knights', 'who', 'say', 'Ni'] >>> Classes >>> class Car: ... def __init__(self): ... self.milespergallon = 25.0 ... self.travelled = 0 ... self.color = 'blue' ... self.gas = 20 ... def drive(self, miles): ... self.travelled = self.travelled + miles ... self.gas = self.gas - (miles / self.milespergallon) ... >>> c = Car() >>> c.drive(100) >>> c.travelled 100 >>> c.gas 16.0 >>> Exception Handling >>> def func1(arg): ... func2(arg) ... >>> def func2(arg): ... func3(arg) ... >>> def func3(arg): ... # this should cause an error ... return arg / 0 ... >>> func1(17) Traceback (most recent call last): File "<interactive input>", line 1, in ? File "<interactive input>", line 2, in func1 File "<interactive input>", line 2, in func2 File "<interactive input>", line 3, in func3 ZeroDivisionError: integer division or modulo by zero >>> The Except Clause The except clause can optionally specify particular error types to look for: >>> try: ... y_scale_factor = plotheight / (dataMax - dataMin) ... except ZeroDivisionError: ... y_scale_factor = 0 ... >>> The Finally Clause The try...finally clause always executes the finally section whether an error occurs or not: >>> f = open('somefile.dat', 'w') >>> try: ... # fetch some data ... # store it in the file ... finally: ... f.close() # make sure file is closed ... # even if errors occurred ... >>> Have you noticed...? ...the lack of block delimiters? 'statement grouping is done by indentation instead of begin/end brackets' Part 4 Integration with Python Integration Tools • • • • • • • • DB API XML Numerous web protocols inc. SOAP and XML-RPC Pyro and others COM CORBA Java - Jython, JPE C/C++ - Python C API, SWIG, CXX, Pyrex... Python Database APIs >>> import mx.ODBC.Windows >>> >>> >>> ... ... >>> conn = mx.ODBC.Windows.connect(DATASOURCE, USER, PASSWORD) cur = conn.cursor() cur.execute("""SELECT JobDescriptionId, DocGroupId, Description FROM RPAJobDescriptions WHERE UserId = 'alice' ORDER BY DocGroupId""") data = cur.fetchall() >>> pprint.pprint(data) [(6, 'multipagesamples', 'simple multipage doc #1'), (7, 'multipagesamples', 'simple multipage doc #2'), (8, 'multipagesamples', 'simple multipage doc #3')] >>> XML Parsing >>> import pyRXP >>> p=pyRXP.Parser() >>> p.parse('<tag1><tag2>content</tag2></tag1>') ('tag1', None, [('tag2', None, ['content'], None)], None) >>> Reading a URL import urllib conn = urllib.urlopen('http://www.reportlab.com/index.html') print conn.headers data = conn.read() Similar high level APIs exist for most internet protocols Integration with C /C++ • • • Python C API: extend or embed SWIG: generate wrappers for C++ code CXX and Pyrex - newer COM Clients >> import win32com.client >> xl = win32.client.Dispatch("Excel.Application") COM Servers class PythonUtilities: _public_methods_ = [ 'SplitString' ] _reg_progid_ = "PythonDemos.Itilities" # Never copy the following ID # use "print pythoncom.CreateGuid()" to make a new one _reg_clsid_ = '{EFC3F30B-FB1B-40BE-8834-00F061BFAA6C}' def SplitString(self, val, item=None): import string if item != None: item = str(item) return string.split(str(val), item) # Add code so that when this script is run by # Python.exe, it self registers. if __name__ == '__main__': print "registering COM server...' import win32com.server.register win32com.server.register.UseCommandLine(PythonUtilities) CORBA • • OmniORB, one of only 3 Corba 2.1 compliant ORBS Wrapper classes are statically generated from IDL files, so you get editor auto-completion on your remote objects :-) CORBA Client >>>import sys,CORBA,Snake >>>orb =CORBA.ORB_init(sys.argv,CORBA.ORB_ID) >>>adder =orb.string_to_object("corbaname:rir:#adder.obj") >>>adder.accumulate(5) 5 >>>adder.accumulate(6) 11 >>>adder.accumulate(42) 53 >>>adder.reset() >>>adder.accumulate(10) 10 CORBA Server import sys,CORBA,CosNaming,Snake,Snake__POA class Adder_i (Snake__POA.Adder): def __init__(self): self.value =0 def accumulate(self,a): self.value =self.value +a return self.value def reset(self): self.value =0 orb = CORBA.ORB_init(sys.argv,CORBA.ORB_ID) poa = orb.resolve_initial_references("RootPOA") adderServant = Adder_i() poa.activate_object(adderServant) adderObjref = adderServant._this() nameRoot = orb.resolve_initial_references("NameService") nameRoot = nameRoot._narrow(CosNaming.NamingContext) name = [CosNaming.NameComponent("adder","obj")] nameRoot.rebind(name,adderObjref) poa._get_the_POAManager().activate() orb.run()27 Java Integration • Jython - Python running on JVM http://www.jython.org/ http://www.jython.org/applets/index.html • JPE - Java Python Bridge (Linking at JNI level) http://jpe.sourceforge.net/ Delphi integration • Complete set of Delphi components which wrap up Python Part 5 Testing with Python Testing Tools • • • • Interactive Prompt Doctest Unittest Crosschecker Interactive Prompt • test functions as you write them DocTest • • comment your code with docstrings showing interactive prompt sessions it replays them and checks the output is as specified unittest • • same as JUnit and pals. Aggregates tests. Testing Approaches Two approaches to testing: • call your APIs to check they work • use Python's glue capabilities to test systems end to end Crosschecker • Commercial product from Secret Labs with an IDE geared to writing and running test cases. Part 6 Conclusion Conclusion Python is the most cost-effective and general way to test systems in other languages - at unit and end-to-end level. • Python http://www.python.org/ • ReportLab http://www.reportlab.com/ • Jython http://www.jython.org/ http://www.jython.org/applets/index.html • JPE http://jpe.sourceforge.net/ • Secret Labs http://www.pythonware.com/