Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7018 Articles
article-image-working-geo-spatial-data-python
Packt
30 Dec 2010
7 min read
Save for later

Working with Geo-Spatial Data in Python

Packt
30 Dec 2010
7 min read
Python Geospatial Development If you want to follow through the examples in this article, make sure you have the following Python libraries installed on your computer: GDAL/OGR version 1.7 or later (http://gdal.org) pyproj version 1.8.6 or later (http://code.google.com/p/pyproj) Shapely version 1.2 or later (http://trac.gispython.org/lab/wiki/Shapely) Reading and writing geo-spatial data In this section, we will look at some examples of tasks you might want to perform that involve reading and writing geo-spatial data in both vector and raster format. Task: Calculate the bounding box for each country in the world In this slightly contrived example, we will make use of a Shapefile to calculate the minimum and maximum latitude/longitude values for each country in the world. This "bounding box" can be used, among other things, to generate a map of a particular country. For example, the bounding box for Turkey would look like this: Start by downloading the World Borders Dataset from:   http://thematicmapping.org/downloads/world_borders.php Decompress the .zip archive and place the various files that make up the Shapefile (the .dbf, .prj, .shp, and .shx files) together in a suitable directory. We next need to create a Python program that can read the borders of each country. Fortunately, using OGR to read through the contents of a Shapefile is trivial: import osgeo.ogr shapefile = osgeo.ogr.Open("TM_WORLD_BORDERS-0.3.shp") layer = shapefile.GetLayer(0) for i in range(layer.GetFeatureCount()): feature = layer.GetFeature(i) The feature consists of a geometry and a set of fields. For this data, the geometry is a polygon that defines the outline of the country, while the fields contain various pieces of information about the country. According to the Readme.txt file, the fields in this Shapefile include the ISO-3166 three-letter code for the country (in a field named ISO3) as well as the name for the country (in a field named NAME). This allows us to obtain the country code and name like this: countryCode = feature.GetField("ISO3") countryName = feature.GetField("NAME") We can also obtain the country's border polygon using: geometry = feature.GetGeometryRef() There are all sorts of things we can do with this geometry, but in this case we want to obtain the bounding box or envelope for the polygon: minLong,maxLong,minLat,maxLat = geometry.GetEnvelope() Let's put all this together into a complete working program: # calcBoundingBoxes.py import osgeo.ogr shapefile = osgeo.ogr.Open("TM_WORLD_BORDERS-0.3.shp") layer = shapefile.GetLayer(0) countries = [] # List of (code,name,minLat,maxLat, # minLong,maxLong) tuples. for i in range(layer.GetFeatureCount()): feature = layer.GetFeature(i) countryCode = feature.GetField("ISO3") countryName = feature.GetField("NAME") geometry = feature.GetGeometryRef() minLong,maxLong,minLat,maxLat = geometry.GetEnvelope() countries.append((countryName, countryCode, minLat, maxLat, minLong, maxLong)) countries.sort() for name,code,minLat,maxLat,minLong,maxLong in countries: print "%s (%s) lat=%0.4f..%0.4f, long=%0.4f..%0.4f" % (name, code,minLat, maxLat,minLong, maxLong) Running this program produces the following output: % python calcBoundingBoxes.py Afghanistan (AFG) lat=29.4061..38.4721, long=60.5042..74.9157 Albania (ALB) lat=39.6447..42.6619, long=19.2825..21.0542 Algeria (DZA) lat=18.9764..37.0914, long=-8.6672..11.9865 ... Task: Save the country bounding boxes into a Shapefile While the previous example simply printed out the latitude and longitude values, it might be more useful to draw the bounding boxes onto a map. To do this, we have to convert the bounding boxes into polygons, and save these polygons into a Shapefile. Creating a Shapefile involves the following steps: Define the spatial reference used by the Shapefile's data. In this case, we'll use the WGS84 datum and unprojected geographic coordinates (that is, latitude and longitude values). This is how you would define this spatial reference using OGR: import osgeo.osr spatialReference = osgeo.osr.SpatialReference() spatialReference.SetWellKnownGeogCS('WGS84') We can now create the Shapefile itself using this spatial reference: import osgeo.ogr driver = osgeo.ogr.GetDriverByName("ESRI Shapefile") dstFile = driver.CreateDataSource("boundingBoxes.shp")) dstLayer = dstFile.CreateLayer("layer", spatialReference) After creating the Shapefile, you next define the various fields that will hold the metadata for each feature. In this case, let's add two fields to store the country name and its ISO-3166 code: fieldDef = osgeo.ogr.FieldDefn("COUNTRY", osgeo.ogr.OFTString) fieldDef.SetWidth(50) dstLayer.CreateField(fieldDef) fieldDef = osgeo.ogr.FieldDefn("CODE", osgeo.ogr.OFTString) fieldDef.SetWidth(3) dstLayer.CreateField(fieldDef) We now need to create the geometry for each feature—in this case, a polygon defining the country's bounding box. A polygon consists of one or more linear rings; the first linear ring defines the exterior of the polygon, while additional rings define "holes" inside the polygon. In this case, we want a simple polygon with a square exterior and no holes: linearRing = osgeo.ogr.Geometry(osgeo.ogr.wkbLinearRing) linearRing.AddPoint(minLong, minLat) linearRing.AddPoint(maxLong, minLat) linearRing.AddPoint(maxLong, maxLat) linearRing.AddPoint(minLong, maxLat) linearRing.AddPoint(minLong, minLat) polygon = osgeo.ogr.Geometry(osgeo.ogr.wkbPolygon) polygon.AddGeometry(linearRing) You may have noticed that the coordinate (minLong, minLat)was added to the linear ring twice. This is because we are defining line segments rather than just points—the first call to AddPoint()defines the starting point, and each subsequent call to AddPoint()adds a new line segment to the linear ring. In this case, we start in the lower-left corner and move counter-clockwise around the bounding box until we reach the lower-left corner again:   Once we have the polygon, we can use it to create a feature: feature = osgeo.ogr.Feature(dstLayer.GetLayerDefn()) feature.SetGeometry(polygon) feature.SetField("COUNTRY", countryName) feature.SetField("CODE", countryCode) dstLayer.CreateFeature(feature) feature.Destroy() Notice how we use the setField() method to store the feature's metadata. We also have to call the Destroy() method to close the feature once we have finished with it; this ensures that the feature is saved into the Shapefile. Finally, we call the Destroy() method to close the output Shapefile: dstFile.Destroy() Putting all this together, and combining it with the code from the previous recipe to calculate the bounding boxes for each country in the World Borders Dataset Shapefile, we end up with the following complete program: # boundingBoxesToShapefile.py import os, os.path, shutil import osgeo.ogr import osgeo.osr # Open the source shapefile. srcFile = osgeo.ogr.Open("TM_WORLD_BORDERS-0.3.shp") srcLayer = srcFile.GetLayer(0) # Open the output shapefile. if os.path.exists("bounding-boxes"): shutil.rmtree("bounding-boxes") os.mkdir("bounding-boxes") spatialReference = osgeo.osr.SpatialReference() spatialReference.SetWellKnownGeogCS('WGS84') driver = osgeo.ogr.GetDriverByName("ESRI Shapefile") dstPath = os.path.join("bounding-boxes", "boundingBoxes.shp") dstFile = driver.CreateDataSource(dstPath) dstLayer = dstFile.CreateLayer("layer", spatialReference) fieldDef = osgeo.ogr.FieldDefn("COUNTRY", osgeo.ogr.OFTString) fieldDef.SetWidth(50) dstLayer.CreateField(fieldDef) fieldDef = osgeo.ogr.FieldDefn("CODE", osgeo.ogr.OFTString) fieldDef.SetWidth(3) dstLayer.CreateField(fieldDef) # Read the country features from the source shapefile. for i in range(srcLayer.GetFeatureCount()): feature = srcLayer.GetFeature(i) countryCode = feature.GetField("ISO3") countryName = feature.GetField("NAME") geometry = feature.GetGeometryRef() minLong,maxLong,minLat,maxLat = geometry.GetEnvelope() # Save the bounding box as a feature in the output # shapefile. linearRing = osgeo.ogr.Geometry(osgeo.ogr.wkbLinearRing) linearRing.AddPoint(minLong, minLat) linearRing.AddPoint(maxLong, minLat) linearRing.AddPoint(maxLong, maxLat) linearRing.AddPoint(minLong, maxLat) linearRing.AddPoint(minLong, minLat) polygon = osgeo.ogr.Geometry(osgeo.ogr.wkbPolygon) polygon.AddGeometry(linearRing) feature = osgeo.ogr.Feature(dstLayer.GetLayerDefn()) feature.SetGeometry(polygon) feature.SetField("COUNTRY", countryName) feature.SetField("CODE", countryCode) dstLayer.CreateFeature(feature) feature.Destroy() # All done. srcFile.Destroy() dstFile.Destroy() The only unexpected twist in this program is the use of a sub-directory called bounding-boxes to store the output Shapefile. Because a Shapefile is actually made up of multiple files on disk (a .dbf file, a .prj file, a .shp file, and a .shx file), it is easier to place these together in a sub-directory. We use the Python Standard Library module shutil to delete the previous contents of this directory, and then os.mkdir() to create it again. If you aren't storing the TM_WORLD_BORDERS-0.3.shp Shapefile in the same directory as the script itself, you will need to add the directory where the Shapefile is stored to your osgeo.ogr.Open() call. You can also store the boundingBoxes.shp Shapefile in a different directory if you prefer, by changing the path where this Shapefile is created. Running this program creates the bounding box Shapefile, which we can then draw onto a map. For example, here is the outline of Thailand along with a bounding box taken from the boundingBoxes.shp Shapefile:  
Read more
  • 0
  • 0
  • 9286

article-image-inheritance-python
Packt
30 Dec 2010
8 min read
Save for later

Inheritance in Python

Packt
30 Dec 2010
8 min read
Python 3 Object Oriented Programming Harness the power of Python 3 objects Learn how to do Object Oriented Programming in Python using this step-by-step tutorial Design public interfaces using abstraction, encapsulation, and information hiding Turn your designs into working software by studying the Python syntax Raise, handle, define, and manipulate exceptions using special error objects Implement Object Oriented Programming in Python using practical examples        Basic inheritance Technically, every class we create uses inheritance. All Python classes are subclasses of the special class named object. This class provides very little in terms of data and behaviors (those behaviors it does provide are all double-underscore methods intended for internal use only), but it does allow Python to treat all objects in the same way. If we don't explicitly inherit from a different class, our classes will automatically inherit from object. However, we can openly state that our class derives from object using the following syntax: class MySubClass(object): pass This is inheritance! Since Python 3 automatically inherits from object if we don't explicitly provide a different superclass. A superclass, or parent class, is a class that is being inherited from. A subclass is a class that is inheriting from a superclass. In this case, the superclass is object, and MySubClass is the subclass. A subclass is also said to be derived from its parent class or that the subclass extends the parent. As you've probably figured out from the example, inheritance requires a minimal amount of extra syntax over a basic class definition. Simply include the name of the parent class inside a pair of parentheses after the class name, but before the colon terminating the class definition. This is all we have to do to tell Python that the new class should be derived from the given superclass. How do we apply inheritance in practice? The simplest and most obvious use of inheritance is to add functionality to an existing class. Let's start with a simple contact manager that tracks the name and e-mail address of several people. The contact class is responsible for maintaining a list of all contacts in a class variable, and for initializing the name and address, in this simple class: class Contact: all_contacts = [] def __init__(self, name, email): self.name = name self.email = email Contact.all_contacts.append(self) This example introduces us to class variables. The all_contacts list, because it is part of the class definition, is actually shared by all instances of this class. This means that there is only one Contact.all_contacts list, and if we call self.all_contacts on any one object, it will refer to that single list. The code in the initializer ensures that whenever we create a new contact, the list will automatically have the new object added. Be careful with this syntax, for if you ever set the variable using self.all_contacts, you will actually be creating a new instance variable on the object; the class variable will still be unchanged and accessible as Contact.all_contacts. This is a very simple class that allows us to track a couple pieces of data about our contacts. But what if some of our contacts are also suppliers that we need to order supplies from? We could add an order method to the Contact class, but that would allow people to accidentally order things from contacts who are customers or family friends. Instead, let's create a new Supplier class that acts like a Contact, but has an additional order method: class Supplier(Contact): def order(self, order): print("If this were a real system we would send " "{} order to {}".format(order, self.name)) Now, if we test this class in our trusty interpreter, we see that all contacts, including suppliers, accept a name and e-mail address in their __init__, but only suppliers have a functional order method: >>> c = Contact("Some Body", "somebody@example.net") >>> s = Supplier("Sup Plier", "supplier@example.net") >>> print(c.name, c.email, s.name, s.email) Some Body somebody@example.net Sup Plier supplier@example.net >>> c.all_contacts [<__main__.Contact object at 0xb7375ecc>, <__main__.Supplier object at 0xb7375f8c>] >>> c.order("Ineed pliers") Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Contact' object has no attribute 'order' >>> s.order("I need pliers") If this were a real system we would send I need pliers order to Supplier >>> So now our Supplier class can do everything a Contact can do (including adding itself to the list of all_contacts) and all the special things it needs to handle as a supplier. This is the beauty of inheritance. Extending built-ins One of the most interesting uses of this kind of inheritance is adding functionality to built-in classes. In the Contact class seen earlier, we are adding contacts to a list of all contacts. What if we also wanted to search that list by name? Well, we could add a method on the Contact class to search it, but it feels like this method actually belongs on the list itself. We can do this using inheritance: class ContactList(list): def search(self, name): '''Return all contacts that contain the search value in their name.''' matching_contacts = [] for contact in self: if name in contact.name: matching_contacts.append(contact) return matching_contacts class Contact: all_contacts = ContactList() def __init__(self, name, email): self.name = name self.email = email self.all_contacts.append(self) Instead of instantiating a normal list as our class variable, we create a new ContactList class that extends the built-in list. Then we instantiate this subclass as our all_contacts list. We can test the new search functionality as follows: >>> c1 = Contact("John A", "johna@example.net") >>> c2 = Contact("John B", "johnb@example.net") >>> c3 = Contact("Jenna C", "jennac@example.net") >>> [c.name for c in Contact.all_contacts.search('John')] ['John A', 'John B'] >>> Are you wondering how we changed the built-in syntax [] into something we can inherit from? Creating an empty list with [] is actually a shorthand for creating an empty list using list(); the two syntaxes are identical: >>> [] == list() True So, the list data type is like a class that we can extend, not unlike object. As a second example, we can extend the dict class, which is the long way of creating a dictionary (the {:} syntax). class LongNameDict(dict): def longest_key(self): longest = None for key in self: if not longest or len(key) > len(longest): longest = key return longest This is easy to test in the interactive interpreter: >>> longkeys = LongNameDict() >>> longkeys['hello'] = 1 >>> longkeys['longest yet'] = 5 >>> longkeys['hello2'] = 'world' >>> longkeys.longest_key() 'longest yet' Most built-in types can be similarly extended. Commonly extended built-ins are object, list, set, dict, file, and str. Numerical types such as int and float are also occasionally inherited from. Overriding and super So inheritance is great for adding new behavior to existing classes, but what about changing behavior? Our contact class allows only a name and an e-mail address. This may be sufficient for most contacts, but what if we want to add a phone number for our close friends? We can do this easily by just setting a phone attribute on the contact after it is constructed. But if we want to make this third variable available on initialization, we have to override __init__. Overriding is altering or replacing a method of the superclass with a new method (with the same name) in the subclass. No special syntax is needed to do this; the subclass's newly created method is automatically called instead of the superclass's method. For example: class Friend(Contact): def __init__(self, name, email, phone): self.name = name self.email = email self.phone = phone Any method can be overridden, not just __init__. Before we go on, however, we need to correct some problems in this example. Our Contact and Friend classes have duplicate code to set up the name and email properties; this can make maintenance complicated, as we have to update the code in two or more places. More alarmingly, our Friend class is neglecting to add itself to the all_contacts list we have created on the Contact class. What we really need is a way to call code on the parent class. This is what the super function does; it returns the object as an instance of the parent class, allowing us to call the parent method directly: class Friend(Contact): def __init__(self, name, email, phone): super().__init__(name, email) self.phone = phone This example first gets the instance of the parent object using super, and calls __init__ on that object, passing in the expected arguments. It then does its own initialization, namely setting the phone attribute. A super() call can be made inside any method, not just __init__. This means all methods can be modified via overriding and calls to super. The call to super can also be made at any point in the method; we don't have to make the call as the first line in the method. For example, we may need to manipulate the incoming parameters before forwarding them to the superclass.  
Read more
  • 0
  • 0
  • 14958

article-image-enhancing-your-site-php-and-jquery
Packt
29 Dec 2010
12 min read
Save for later

Enhancing your Site with PHP and jQuery

Packt
29 Dec 2010
12 min read
  PHP jQuery Cookbook Over 60 simple but highly effective recipes to create interactive web applications using PHP with jQuery Create rich and interactive web applications with PHP and jQuery Debug and execute jQuery code on a live site Design interactive forms and menus Another title in the Packt Cookbook range, which will help you get to grips with PHP as well as jQuery         Read more about this book       (For more resources on this subject, see here.) Introduction In this article, we will look at some advanced techniques that can be used to enhance the functionality of web applications. We will create a few examples where we will search for images the from Flickr and videos from YouTube using their respective APIs. We will parse a RSS feed XML using jQuery and learn to create an endless scrolling page like Google reader or the new interface of Twitter. Besides this, you will also learn to create a jQuery plugin, which you can use independently in your applications. Sending cross-domain requests using server proxy Browsers do not allow scripts to send cross-domain requests due to security reasons. This means a script at domain http://www.abc.com cannot send AJAX requests to http://www.xyz.com. This recipe will show how you can overcome this limitation by using a PHP script on the server side. We will create an example that will search Flickr for images. Flickr will return a JSON, which will be parsed by jQuery and images will be displayed on the page. The following screenshot shows a JSON response from Flickr: Getting ready Create a directory for this article and name it as Article9. In this directory, create a folder named Recipe1. Also get an API key from Flickr by signing up at http://www.flickr.com/services/api/keys/. How to do it... Create a file inside the Recipe1 folder and name it as index.html. Write the HTML code to create a form with three fields: tag, number of images, and image size. Also create an ul element inside which the results will be displayed. <html> <head> <title>Flickr Image Search</title> <style type="text/css"> body { font-family:"Trebuchet MS",verdana,arial;width:900px; } fieldset { width:333px; } ul{ margin:0;padding:0;list-style:none; } li{ padding:5px; } span{ display:block;float:left;width:150px; } #results li{ float:left; } .error{ font-weight:bold; color:#ff0000; } </style> </head> <body> <form id="searchForm"> <fieldset> <legend>Search Criteria</legend> <ul> <li> <span>Tag</span> <input type="text" name="tag" id="tag"/> </li> <li> <span>Number of images</span> <select name="numImages" id="numImages"> <option value="20">20</option> <option value="30">30</option> <option value="40">40</option> <option value="50">50</option> </select> </li> <li> <span>Select a size</span> <select id="size"> <option value="s">Small</option> <option value="t">Thumbnail</option> <option value="-">Medium</option> <option value="b">Large</option> <option value="o">Original</option> </select> </li> <li> <input type="button" value="Search" id="search"/> </li> </ul> </fieldset> </form> <ul id="results"> </ul> </body> </html> The following screenshot shows the form created: Include the jquery.js file. Then, enter the jQuery code that will send the AJAX request to a PHP file search.php. Values of form elements will be posted with an AJAX request. A callback function showImages is also defined that actually reads the JSON response and displays the images on the page. <script type="text/javascript" src="../jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { $('#search').click(function() { if($.trim($('#tag').val()) == '') { $('#results').html('<li class="error">Please provide search criteria</li>'); return; } $.post( 'search.php', $('#searchForm').serialize(), showImages, 'json' ); }); function showImages(response) { if(response['stat'] == 'ok') { var photos = response.photos.photo; var str= ''; $.each(photos, function(index,value) { var farmId = value.farm; var serverId = value.server; var id = value.id; var secret = value.secret; var size = $('#size').val(); var title = value.title; var imageUrl = 'http://farm' + farmId + '.static.flickr.com/' + serverId + '/' + id + '_' + secret + '_' + size + '.jpg'; str+= '<li>'; str+= '<img src="' + imageUrl + '" alt="' + title + '" />'; str+= '</li>'; }); $('#results').html(str); } else { $('#results').html('<li class="error">an error occured</li>'); } } }); </script> Create another file named search.php. The PHP code in this file will contact the Flickr API with specified search criteria. Flickr will return a JSON that will be sent back to the browser where jQuery will display it on the page. <?php define('API_KEY', 'your-API-key-here'); $url = 'http://api.flickr.com/services/rest/?method=flickr. photos.search'; $url.= '&api_key='.API_KEY; $url.= '&tags='.$_POST['tag']; $url.= '&per_page='.$_POST['numImages']; $url.= '&format=json'; $url.= '&nojsoncallback=1'; header('Content-Type:text/json;'); echo file_get_contents($url); ?> Now, run the index.html file in your browser, enter a tag to search in the form, and select the number of images to be retrieved and image size. Click on the Search button. A few seconds later you will see the images from Flickr displayed on the page: <html> <head> <title>Youtube Video Search</title> <style type="text/css"> body { font-family:"Trebuchet MS",verdana,arial;width:900px; } fieldset { width:333px; } ul{ margin:0;padding:0;list-style:none; } li{ padding:5px; } span{ display:block;float:left;width:150px; } #results ul li{ float:left; background-color:#483D8B; color:#fff;margin:5px; width:120px; } .error{ font-weight:bold; color:#ff0000; } img{ border:0} </style> </head> <body> <form id="searchForm"> <fieldset> <legend>Search Criteria</legend> <ul> <li> <span>Enter query</span> <input type="text" id="query"/> </li> <li> <input type="button" value="Search" id="search"/> </li> </ul> </fieldset> </form> <div id="results"> </div> </body> </html> How it works... On clicking the Search button, form values are sent to the PHP file search.php. Now, we have to contact Flickr and search for images. Flickr API provides several methods for accessing images. We will use the method flickr.photos.search to search by tag name. Along with method name we will have to send the following parameters in the URL: api_key: An API key is mandatory. You can get one from: http://www.flickr.com/services/api/keys/. tags: The tags to search for. These can be comma-separated. This value will be the value of textbox tag. per_page: Number of images in a page. This can be a maximum of 99. Its value will be the value of select box numImages. format: It can be JSON, XML, and so on. For this example, we will use JSON. nojsoncallback: Its value will be set to 1 if we don't want Flickr to wrap the JSON in a function wrapper. Once the URL is complete we can contact Flickr to get results. To get the results' we will use the PHP function file_get_contents, which will get the results JSON from the specified URL. This JSON will be echoed to the browser. jQuery will receive the JSON in callback function showImages. This function first checks the status of the response. If the response is OK, we get the photo elements from the response and we can iterate over them using jQuery's $.each method. To display an image, we will have to get its URL first, which will be created by combining different values of the photo object. According to Flickr API specification, an image URL can be constructed in the following manner: http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[size].jpg So we get the farmId, serverId, id, and secret from the photo element. The size can be one of the following: s (small square) t (thumbnail) - (medium) b (large) o (original image) We have already selected the image size from the select box in the form. By combining all these values, we now have the Flickr image URL. We wrap it in a li element and repeat the process for all images. Finally, we insert the constructed images into the results li. Making cross-domain requests with jQuery The previous recipe demonstrated the use of a PHP file as a proxy for querying cross-domain URLs. This recipe will show the use of JSONP to query cross-domain URLs from jQuery itself. We will create an example that will search for the videos from YouTube and will display them in a list. Clicking on a video thumbnail will open a new window that will take the user to the YouTube website to show that video. The following screenshot shows a sample JSON response from YouTube: Getting ready Create a folder named Recipe2 inside the Article9 directory. How to do it... Create a file inside the Recipe2 folder and name it as index.html. Write the HTML code to create a form with a single field query and a DIV with results ID inside which the search results will be displayed. <script type="text/javascript" src="../jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { $('#search').click(function() { var query = $.trim($('#query').val()); if(query == '') { $('#results').html('<li class="error">Please enter a query.</li>'); return; } $.get( 'http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script', {}, showVideoList, 'jsonp' ); }); }); function showVideoList(response) { var totalResults = response['feed']['openSearch$totalResults']['$t']; if(parseInt(totalResults,10) > 0) { var entries = response.feed.entry; var str = '<ul>'; for(var i=1; i< entries.length; i++) { var value = entries[i]; var title = value['title']['$t']; var mediaGroup = value['media$group']; var videoURL = mediaGroup['media$player'][0]['url']; var thumbnail = mediaGroup['media$thumbnail'][0]['url']; var thumbnailWidth = mediaGroup['media$thumbnail'][0]['width']; var thumbnailHeight = mediaGroup['media$thumbnail'][0]['height']; var numComments = value['gd$comments']['gd$feedLink']['countHint']; var rating = parseFloat(value['gd$rating']['average']).toFixed(2); str+= '<li>'; str+= '<a href="' + videoURL + '" target="_blank">'; str+= '<img src="'+thumbNail+'" width="'+thumbNailWidth+'" height="'+thumbNailWidth+'" title="' + title + '" />'; str+= '</a>'; str+= '<hr>'; str+= '<p style="width: 120px; font-size: 12px;">Comments: ' + numComments + ''; str+= '<br/>'; str+= 'Rating: ' + rating; str+= '</p>'; str+= '</li>'; } str+= '</ul>'; $('#results').html(str); } else { $('#results').html('<li class="error">No results.</li>'); } } </script> Include the jquery.js file before closing the &ltbody> tag. Now, write the jQuery code that will take the search query from the textbox and will try to retrieve the results from YouTube. A callback function called showVideoList will get the response and will create a list of videos from the response. http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script All done, and we are now ready to search YouTube. Run the index.html file in your browser and enter a search query. Click on the Search button and you will see a list of videos with a number of comments and a rating for each video. How it works... script tags are an exception to cross-browser origin policy. We can take advantage of this by requesting the URL from the src attribute of a script tag and by wrapping the raw response in a callback function. In this way the response becomes JavaScript code instead of data. This code can now be executed on the browser. The URL for YouTube video search is as follows: http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script Parameter q is the query that we entered in the textbox and alt is the type of response we want. Since we are using JSONP instead of JSON, the value for alt is defined as json-in-script as per YouTube API specification. On getting the response, the callback function showVideoList executes. It checks whether any results are available or not. If none are found, an error message is displayed. Otherwise, we get all the entry elements and iterate over them using a for loop. For each video entry, we get the videoURL, thumbnail, thumbnailWidth, thumbnailHeight, numComments, and rating. Then we create the HTML from these variables with a list item for each video. For each video an anchor is created with href set to videoURL. The video thumbnail is put inside the anchor and a p tag is created where we display the number of comments and rating for a particular video. After the HTML has been created, it is inserted in the DIV with ID results. There's more... About JSONP You can read more about JSONP at the following websites: http://remysharp.com/2007/10/08/what-is-jsonp/ http://en.wikipedia.org/wiki/JSON#JSONP
Read more
  • 0
  • 0
  • 8009

article-image-cocos2d-iphone-surfing-through-scenes
Packt
29 Dec 2010
10 min read
Save for later

Cocos2d for iPhone: Surfing Through Scenes

Packt
29 Dec 2010
10 min read
  Cocos2d for iPhone 0.99 Beginner's Guide Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework! A cool guide to learning cocos2d with iPhone to get you into the iPhone game industry quickly Learn all the aspects of cocos2d while building three different games Add a lot of trendy features such as particles and tilemaps to your games to captivate your players Full of illustrations, diagrams, and tips for building iPhone games, with clear step-by-step instructions and practical examples         Read more about this book       (For more resources on Cocos2d, see here.) We'll be doing a lot of things in this article, so let's get started. Aerial Gun, a vertical shooter game Let's talk about the game we will be making. Aerial Gun, as I said earlier, is a vertical shooter game. That means you will be in control of an airship which will move vertically. Actually, the airship won't be moving anywhere (well except to the left and right); what is really going to move here is the background, giving the sense the airship is the one moving. The player will be able to control the airship by using accelerometer controls. We'll also provide some areas where the player can touch to fire bullets or bombs to destroy the enemies. Enemies will be appearing at different time intervals and will have some different behaviors attached to them. For example, some of them could just move towards the airship, others may stay there and shoot back, and so on. We'll do it in a way you can create more custom behaviors later. For this game we will be using the Cocos2d template again, just because it is the easier way to have a project properly set up, at least to begin our work. So, follow the same steps as when we created the Coloured Stones game. Just open Xcode and go to the File menu, select to create a new project. Then choose the Cocos2d template (the simple one, without any physics engine) and give a name to the project. I will call it AerialGun. Once you do that, your new project should be opened. Creating new scenes We will begin this new game by first doing a couple of scenes other than the one that holds the actual game. If you take a look at the class that the template generates, at first sight you won't find what would appear to be a scene. That is because the template handles that in a confusing fashion, at least for people who are just getting started with Cocos2d. Let's take a look at that. Open the newly generated HelloWorldScene.m file. This is the same code in which we based our first game. This time we will analyze it so you understand what it is doing behind scenes. Take a look at the first method of the implementation of the HelloWorld Layer: +(id) scene{ // 'scene' is an autorelease object. CCScene *scene = [CCScene node]; // 'layer' is an autorelease object. HelloWorld *layer = [HelloWorld node]; // add layer as a child to scene [scene addChild: layer]; // return the scene return scene;} If you remember well, this is the method that get's called in the AppDelegate when the Director needs to start running with a scene. So why are we passing the method of a layer instead of an instance of a CCScene class? If you pay attention to the +(id)scene method of the layer, what it does is to instantiate a CCScene object, then it instantiates a HelloWorld object (the layer), and then it adds that layer to the scene and returns it. This actually works as you have witnessed and is pretty quick to set up, and get you up and running. However, sometimes you will need to have you own custom scenes, that is a class that inherits from CCScene and extends it in some fashion. Now we are going to create some of the scenes that we need for this game and add a layer to each one. Then when it is time to work with the game scene we will modify it to match our needs. Before doing anything, please change the name of the HelloWorldlayer to GameLayer and the name of the HelloWorldScene to GameScene, just as we did back then with our first game. Time for action – creating the splash and main menu scene We will begin with a simple scene, the splash screen. A splash screen is the first thing that appears as you launch a game. Well, the second if you count the Default.png image. The objective of this screen is to show the player some information about the developer, other companies that helped, and so on. Most times you will just show a couple and logos and move to the main menu. So, what we will do now is put a new Default.png image, then create the SplashScene. The Default.png image is located in your project's Resources group folder and it is the first image that is shown when the application is launched. You should always have one, so that something is shown while the application is being launched. This happens before anything is initialized, so you can't do anything else other than show this image. Its dimensions must be 320 * 480 (when developing for older generation iPhones), so if your game is supposed to be in landscape mode, you should rotate the image with any graphics software before including it in your project. The SplashScene is going to show a sprite for a moment, then fade it, show another one and then move to the GameScene. Let's add a new file to the project. Select New File (CMD + N) from the File menu, then select Objective-C class (we are going to do it from scratch anyways) and name it SplashScene. This file will hold both the SplashScene and the SplashLayer classes. The SplashLayer will be added as a child of the SplashScene, and inside the layer we will add the sprites. Before writing any code, add to the project the three splash images I created for you. You can find them in the companion files folder. Once you have them added into your project we can begin working on the SplashScene. The following is the SplashScene.h: #import <Foundation/Foundation.h>#import "cocos2d.h"#import "MainMenuScene.h"@interface SplashScene : CCScene { }@end@interface SplashLayer : CCLayer { }@end And the SplashScene.m file: #import "SplashScene.h" //Here is the implementation of the SplashScene@implementation SplashScene- (id) init{ self = [super init]; if (self != nil) { [self addChild:[SplashLayer node]]; } return self;}-(void)dealloc{ [super dealloc];}@end//And here is the implementation of the SplashLayer@implementation SplashLayer- (id) init{ if ((self = [super init])) { isTouchEnabled = YES; NSMutableArray * splashImages = [[NSMutableArray alloc]init]; for(int i =1;i<=3;i++) { CCSprite * splashImage = [CCSprite spriteWithFile:[NSString stringWithFormat:@"splash%d.png",i]]; [splashImage setPosition:ccp(240,160)]; [self addChild:splashImage]; if(i!=1) [splashImage setOpacity:0]; [splashImages addObject:splashImage]; } [self fadeAndShow:splashImages]; } return self;}//Now we add the methods that handle the image switching-(void)fadeAndShow:(NSMutableArray *)images{ if([images count]<=1) { [images release]; [[CCDirector sharedDirector]replaceScene:[MainMenuScene node]]; } else { CCSprite * actual = (CCSprite *)[images objectAtIndex:0]; [images removeObjectAtIndex:0]; CCSprite * next = (CCSprite *)[images objectAtIndex:0]; [actual runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2], [CCFadeOut actionWithDuration:1],[CCCallFuncN actionWithTarget:self selector:@selector(remove:)],nil]]; [next runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2], [CCFadeIn actionWithDuration:1],[CCDelayTime actionWithDuration:2], [CCCallFuncND actionWithTarget:self selector:@selector(cFadeAndShow: data:) data:images],nil]]; } }-(void) cFadeAndShow:(id)sender data:(void*)data{ NSMutableArray * images = (NSMutableArray *)data; [self fadeAndShow:images];}-(void)remove:(CCSprite *)s{ [s.parent removeChild:s cleanup:YES];}-(void)dealloc{ [super dealloc];}@end As you may notice, I have put together two classes in only one file. You can do this with any number of classes, as long you don't get lost in such a long file. Generally, you will create a pair of files (the .m and .h) for each class, but as the SplashScene class has very little code, I'd rather put it there. For this to work properly, we need to make some other changes. First, open the AerialGunAppDelegate.m file and change the line where the the Director starts running the GameScene. We want it to start by running the SplashScene now. So replace that line with the following: [[CCDirector sharedDirector] runWithScene:[SplashScene node]]; Also remember to import your SplashScene.h file in order for the project to properly compile. Finally, we have to create the MainMenuScene, which is the scene the Director will run after the last image has faded out. So, let's create that one and leave it blank for now. The following is the MainMenuScene.h file: #import <Foundation/Foundation.h>#import "cocos2d.h"@interface MainMenuScene : CCScene { }@end@interface MainMenuLayer : CCLayer { }@end The following is the MainMenuScene.m file: #import "MainMenuScene.h"@implementation MainMenuScene- (id) init{ self = [super init]; if (self != nil) { [self addChild:[MainMenuLayer node]]; } return self;}-(void)dealloc{ [super dealloc];}@end@implementation MainMenuLayer- (id) init{ if ((self = [super init])) { isTouchEnabled = YES; } return self;}-(void)dealloc{ [super dealloc];}@end That would be all for now. Run the project and you should see the first image appear. Then fade to the next one after a while and continue till the last one. Once the last one has faded out, we move on to the MainMenuScene. What just happened? Creating a new scene is as easy as that. You can see from the MainMenuScene code that what we are doing is quite simple; when the MainMenuScene gets created we just instantiate the MainMenuLayer and add it to the scene. That layer is where we will later add all the necessary logic for the main menu. The MainMenuScene as well as the SplashScene both inherit from the CCScene class. This class is just another CCNode. Let's take a little look at the logic behind the SplashScene: NSMutableArray * splashImages = [[NSMutableArray alloc]init];for(int i =1;i<=3;i++){ CCSprite * splashImage = [CCSprite spriteWithFile:[NSString stringWithFormat:@"splash%d.png",i]]; [splashImage setPosition:ccp(240,160)]; [self addChild:splashImage]; if(i!=1) [splashImage setOpacity:0]; [splashImages addObject:splashImage];} [self fadeAndShow:splashImages]; This piece of code is from the SplashLayer init method. What we are doing here is creating an array that will hold any amount of sprites (in this case, three of them). Then we create and add those sprites to it. Finally, the fadeAndShow method is called, passing that newly created array to it. The fadeAndShow method is responsible for fading the images it has. It grabs the first image in the array (after that, the sprite is removed from the array). It then grabs the next one. Then it applies actions to both of them to fade them in and out. The last action of the second sprite's action is a CCCallFuncND, which we use to call the fadeAndShow method again with the modified array. This occurs if there is more than one remaining sprite in the array. If there isn't, the fadeAndShow method calls the Director's replaceScene method with the MainMenuScene. I have made the SplashLayer logic, so you can add more splash images to the array (or leave just one) if you like. Generally, just one or two images will be more than enough.
Read more
  • 0
  • 0
  • 9208

article-image-geo-spatial-data-python-working-geometry
Packt
29 Dec 2010
8 min read
Save for later

Geo-Spatial Data in Python: Working with Geometry

Packt
29 Dec 2010
8 min read
Python Geospatial Development Build a complete and sophisticated mapping application from scratch using Python tools for GIS development Build applications for GIS development using Python Analyze and visualize Geo-Spatial data Comprehensive coverage of key GIS concepts Recommended best practices for storing spatial data in a database Draw maps, place data points onto a map, and interact with maps A practical tutorial with plenty of step-by-step instructions to help you develop a mapping application from scratch         Read more about this book       (For more resources on Python, see here.) Working with Shapely geometries Shapely is a very capable library for performing various calculations on geo-spatial data. Let's put it through its paces with a complex, real-world problem. Task: Identify parks in or near urban areas The U.S. Census Bureau makes available a Shapefile containing something called Core Based Statistical Areas (CBSAs), which are polygons defining urban areas with a population of 10,000 or more. At the same time, the GNIS website provides lists of placenames and other details. Using these two datasources, we will identify any parks within or close to an urban area. Because of the volume of data we are potentially dealing with, we will limit our search to California. Feel free to download the larger data sets if you want, though you will have to optimize the code or your program will take a very long time to check all the CBSA polygon/placename combinations. Let's start by downloading the necessary data. Go to the TIGER website at http://census.gov/geo/www/tiger Click on the 2009 TIGER/Line Shapefiles Main Page link, then follow the Download the 2009 TIGER/Line Shapefiles now link. Choose California from the pop-up menu on the right, and click on Submit. A list of the California Shapefiles will be displayed; the Shapefile you want is labelled Metropolitan/Micropolitan Statistical Area. Click on this link, and you will download a file named tl_2009_06_cbsa.zip. Once the file has downloaded, uncompress it and place the resulting Shapefile into a convenient location so that you can work with it. You now need to download the GNIS placename data for California. Go to the GNIS website: http://geonames.usgs.gov/domestic Click on the Download Domestic Names hyperlink, and then choose California from the pop-up menu. You will be prompted to save the CA_Features_XXX.zip file. Do so, then decompress it and place the resulting CA_Features_XXX.txt file into a convenient place. The XXX in the above file name is a date stamp, and will vary depending on when you download the data. Just remember the name of the file as you'll need to refer to it in your source code. We're now ready to write the code. Let's start by reading through the CBSA urban area Shapefile and extracting the polygons that define the boundary of each urban area: shapefile = osgeo.ogr.Open("tl_2009_06_cbsa.shp")layer = shapefile.GetLayer(0)for i in range(layer.GetFeatureCount()): feature = layer.GetFeature(i) geometry = feature.GetGeometryRef() ... Make sure you add directory paths to your osgeo.ogr.Open() statement (and to the file() statement below) to match where you've placed these files. Using what we learned in the previous section, we can convert this geometry into a Shapely object so that we can work with it: wkt = geometry.ExportToWkt()shape = shapely.wkt.loads(wkt) Next, we need to scan through the CA_Features_XXX.txt file to identify the features marked as a park. For each of these features, we want to extract the name of the feature and its associated latitude and longitude. Here's how we might do this: f = file("CA_Features_XXX.txt", "r")for line in f.readlines(): chunks = line.rstrip().split("|") if chunks[2] == "Park": name = chunks[1] latitude = float(chunks[9]) longitude = float(chunks[10]) ... Remember that the GNIS placename database is a pipedelimited text file. That's why we have to split the line up using line.rstrip().split("|"). Now comes the fun part—we need to figure out which parks are within or close to each urban area. There are two ways we could do this, either of which will work: We could use the shape.distance() method to calculate the distance between the shape and a Point object representing the park's location: We could dilate the polygon using the shape.buffer() method, and then see if the resulting polygon contained the desired point: The second option is faster when dealing with a large number of points as we can pre-calculate the dilated polygons and then use them to compare against each point in turn. Let's take this option: # findNearbyParks.pyimport osgeo.ogrimport shapely.geometryimport shapely.wktMAX_DISTANCE = 0.1 # Angular distance; approx 10 km.print "Loading urban areas..."urbanAreas = {} # Maps area name to Shapely polygon.shapefile = osgeo.ogr.Open("tl_2009_06_cbsa.shp")layer = shapefile.GetLayer(0)for i in range(layer.GetFeatureCount()): feature = layer.GetFeature(i) name = feature.GetField("NAME") geometry = feature.GetGeometryRef() shape = shapely.wkt.loads(geometry.ExportToWkt()) dilatedShape = shape.buffer(MAX_DISTANCE) urbanAreas[name] = dilatedShapeprint "Checking parks..."f = file("CA_Features_XXX.txt", "r")for line in f.readlines(): chunks = line.rstrip().split("|") if chunks[2] == "Park": parkName = chunks[1] latitude = float(chunks[9]) longitude = float(chunks[10]) pt = shapely.geometry.Point(longitude, latitude) for urbanName,urbanArea in urbanAreas.items(): if urbanArea.contains(pt): print parkName + " is in or near " + urbanNamef.close() Don't forget to change the name of the CA_Features_XXX.txt file to match the actual name of the file you downloaded. You may also need to change the path names to the tl_2009_06_CBSA.shp file and the CA_Features file if you placed them in a different directory. If you run this program, you will get a master list of all the parks that are in or close to an urban area: % python findNearbyParks.pyLoading urban areas...Checking parks...Imperial National Wildlife Refuge is in or near El Centro, CATwinLakesStateBeach is in or near Santa Cruz-Watsonville, CAAdmiralWilliamStandleyState Recreation Area is in or near Ukiah, CAAgate Beach County Park is in or near San Francisco-Oakland-Fremont, CA... Note that our program uses angular distances to decide if a park is in or near a given urban area. An angular distance is the angle (in decimal degrees) between two rays going out from the center of the Earth to the Earth's surface. Because a degree of angular measurement (at least for the latitudes we are dealing with here) roughly equals 100 km on the Earth's surface, an angular measurement of 0.1 roughly equals a real distance of 10 km. Using angular measurements makes the distance calculation easy and quick to calculate, though it doesn't give an exact distance on the Earth's surface. If your application requires exact distances, you could start by using an angular distance to filter out the features obviously too far away, and then obtain an exact result for the remaining features by calculating the point on the polygon's boundary that is closest to the desired point, and then calculating the linear distance between the two points. You would then discard the points that exceed your desired exact linear distance. Converting and standardizing units of geometry and distance Imagine that you have two points on the Earth's surface with a straight line drawn between them: Each point can be described as a coordinate using some arbitrary coordinate system (for example, using latitude and longitude values), while the length of the straight line could be described as the distance between the two points. Given any two coordinates, it is possible to calculate the distance between them. Conversely, you can start with one coordinate, a desired distance and a direction, and then calculate the coordinates for the other point. Of course, because the Earth's surface is not flat, we aren't really dealing with straight lines at all. Rather, we are calculating geodetic or Great Circle distances across the surface of the Earth. The pyproj Python library allows you to perform these types of calculations for any given datum. You can also use pyproj to convert from projected coordinates back to geographic coordinates, and vice versa, allowing you to perform these sorts of calculations for any desired datum, coordinate system, and projection. Ultimately, a geometry such as a line or a polygon consists of nothing more than a list of connected points. This means that, using the above process, you can calculate the geodetic distance between each of the points in any polygon and total the results to get the actual length for any geometry. Let's use this knowledge to solve a realworld problem.  
Read more
  • 0
  • 0
  • 10495

article-image-python-testing-mock-objects
Packt
28 Dec 2010
13 min read
Save for later

Python Testing: Mock Objects

Packt
28 Dec 2010
13 min read
How to install Python Mocker Python Mocker isn't included in the standard Python distribution. That means that we need to download and install it. Time for action – installing Python Mocker At the time of this writing, Python Mocker's home page is located at http://labix.org/mocker, while its downloads are hosted at https://launchpad.net/mocker/+download. Go ahead and download the newest version, and we'll see about installing it. The first thing that needs to be done is to unzip the downloaded file. It's a .tar.bz2, which should just work for Unix, Linux, or OSX users. Windows users will need a third-party program (7-Zip works well: http://www.7-zip.org/) to uncompress the archive. Store the uncompressed file in some temporary location. Once you have the files unzipped somewhere, go to that location via the command line. Now, to do this next step, you either need to be allowed to write files into your Python installation's site-packages directory (which you are, if you're the one who installed Python in the first place) or you need to be using Python version 2.6 or higher. If you can write to site-packages, type $ python setup.py install If you can't write to site-packages, but you're using Python 2.6 or higher, type $ python setup.py install --user Sometimes, a tool called easy_install can simplify the installation process of Python modules and packages. If you want to give it a try, download and install setuptools from http://pypi.python.org/pypi/setuptools, according to the directions on that page, and then run the command easy_install mocker. Once that command is done, you should be ready to use Nose. Once you have successfully run the installer, Python Mocker is ready for use. What is a mock object in software testing? "Mock" in this sense means "imitation," and that's exactly what a mock object does. Mock objects imitate the real objects that make up your program, without actually being those objects or relying on them in any way. Instead of doing whatever the real object would do, a mock object performs predefined simple operations that look like what the real object should do. That means its methods return appropriate values (which you told it to return) or raise appropriate exceptions (which you told it to raise). A mock object is like a mockingbird; imitating the calls of other birds without comprehending them. We've already used one mock object in our earlier work when we replaced time.time with an object (in Python, functions are objects) that returned an increasing series of numbers. The mock object was like time.time, except that it always returned the same series of numbers, no matter when we ran our test or how fast the computer was that we ran it on. In other words, it decoupled our test from an external variable. That's what mock objects are all about: decoupling tests from external variables. Sometimes those variables are things like the external time or processor speed, but usually the variables are the behavior of other units. Python Mocker The idea is pretty straightforward, but one look at that mock version of time.time shows that creating mock objects without using a toolkit of some sort can be a dense and annoying process, and can interfere with the readability of your tests. This is where Python Mocker (or any of several other mock object toolkits, depending on preference) comes in. Time for action – exploring the basics of Mocker We'll walk through some of the simplest—and most useful—features of Mocker. To do that, we'll write tests that describe a class representing a specific mathematical operation (multiplication) which can be applied to the values of arbitrary other mathematical operation objects. In other words, we'll work on the guts of a spreadsheet program (or something similar). We're going to use Mocker to create mock objects to stand in place of the real operation objects. Create up a text file to hold the tests, and add the following at the beginning (assuming that all the mathematical operations will be defined in a module called operations): >>> from mocker import Mocker >>> import operations We've decided that every mathematical operation class should have a constructor accepting the objects representing the new object's operands. It should also have an evaluate function that accepts a dictionary of variable bindings as its parameter and returns a number as the result. We can write the tests for the constructor fairly easily, so we do that first (Note that we've included some explanation in the test file, which is always a good idea): We're going to test out the constructor for the multiply operation, first. Since all that the constructor has to do is record all of the operands, this is straightforward. >>> mocker = Mocker() >>> p1 = mocker.mock() >>> p2 = mocker.mock() >>> mocker.replay() >>> m = operations.multiply(p1, p2) >>> m.operands == (p1, p2) True >>> mocker.restore() >>> mocker.verify() The tests for the evaluate method are somewhat more complicated, because there are several things we need to test. This is also where we start seeing the real advantages of Mocker: Now we're going to check the evaluate method for the multiply operation. It should raise a ValueError if there are less than two operands, it should call the evaluate methods of all operations that are operands of the multiply, and of course it should return the correct value. >>> mocker = Mocker() >>> p1 = mocker.mock() >>> p1.evaluate({}) #doctest: +ELLIPSIS <mocker.Mock object at ...> >>> mocker.result(97.43) >>> mocker.replay() >>> m = operations.multiply(p1) >>> m.evaluate({}) Traceback (most recent call last): ValueError: multiply without at least two operands is meaningless >>> mocker.restore() >>> mocker.verify() >>> mocker = Mocker() >>> p1 = mocker.mock() >>> p1.evaluate({}) #doctest: +ELLIPSIS <mocker.Mock object at ...> >>> mocker.result(97.43) >>> p2 = mocker.mock() >>> p2.evaluate({}) #doctest: +ELLIPSIS <mocker.Mock object at ...> >>> mocker.result(-16.25) >>> mocker.replay() >>> m = operations.multiply(p1, p2) >>> round(m.evaluate({}), 2) -1583.24 >>> mocker.restore() >>> mocker.verify() If we run the tests now, we get a list of failed tests. Most of them are due to Mocker being unable to import the operations module, but the bottom of the list should look like this: Finally, we'll write some code in the operations module that passes these tests, producing the following: class multiply: def __init__(self, *operands): self.operands = operands def evaluate(self, bindings): vals = [x.evaluate(bindings) for x in self.operands] if len(vals) < 2: raise ValueError('multiply without at least two ' 'operands is meaningless') result = 1.0 for val in vals: result *= val return result Now when we run the tests, none of them should fail. What just happened? The difficulty in writing the tests for something like this comes(as it often does) from the need to decouple the multiplication class from all of the other mathematical operation classes, so that the results of the multiplication test only depend on whether multiplication works correctly. We addressed this problem by using the Mocker framework for mock objects. The way Mocker works is that you first create an object representing the mocking context, by doing something such as mocker = Mocker(). The mocking context will help you create mock objects, and it will store information about how you expect them to be used. Additionally, it can help you temporarily replace library objects with mocks (like we've previously done with time.time) and restore the real objects to their places when you're done. We'll see more about doing that in a little while. Once you have a mocking context, you create a mock object by calling its mock method, and then you demonstrate how you expect the mock objects to be used. The mocking context records your demonstration, so later on when you call its replay method it knows what usage to expect for each object and how it should respond. Your tests (which use the mock objects instead of the real objects that they imitate), go after the call to replay. Finally, after test code has been run, you call the mocking context's restore method to undo any replacements of library objects, and then verify to check that the actual usage of the mocks was as expected. Our first use of Mocker was straightforward. We tested our constructor, which is specified to be extremely simple. It's not supposed to do anything with its parameters, aside from store them away for later. Did we gain anything at all by using Mocker to create mock objects to use as the parameters, when the parameters aren't even supposed to do anything? In fact, we did. Since we didn't tell Mocker to expect any interactions with the mock objects, it will report nearly any usage of the parameters (storing them doesn't count, because storing them isn't actually interacting with them) as errors during the verify step. When we call mocker.verify(), Mocker looks back at how the parameters were really used and reports a failure if our constructor tried to perform some action on them. It's another way to embed our expectations into our tests. We used Mocker twice more, except in those later uses we told Mocker to expect a call to an evaluate method on the mock objects (i.e. p1 and p2), and to expect an empty dictionary as the parameter to each of the mock objects' evaluate call. For each call we told it to expect, we also told it that its response should be to return a specific floating point number. Not coincidentally, that mimics the behavior of an operation object, and we can use the mocks in our tests of multiply.evaluate. If multiply.evaluate hadn't called the evaluate methods of mock, or if it had called one of them more than once, our mocker.verify call would have alerted us to the problem. This ability to describe not just what should be called but how often each thing should be called is a very useful too that makes our descriptions of what we expect much more complete. When multiply.evaluate calls the evaluate method of mock, the values that get returned are the ones that we specified, so we know exactly what multiply.evaluate ought to do. We can test it thoroughly, and we can do it without involving any of the other units of our code. Try changing how multiply.evaluate works and see what mocker.verify says about it. Mocking functions Normal objects (that is to say, objects with methods and attributes created by instantiating a class) aren't the only things you can make mocks of. Functions are another kind of object that can be mocked, and it turns out to be pretty easy. During your demonstration, if you want a mock object to represent a function, just call it. The mock object will recognize that you want it to behave like a function, and it will make a note of what parameters you passed it, so that it can compare them against what gets passed to it during the test. For example, the following code creates a mock called func, which pretends to be a function that, when called once with the parameters 56 and hello, returns the number 11. The second part of the example uses the mock in a very simple test: >>> from mocker import Mocker >>> mocker = Mocker() >>> func = mocker.mock() >>> func(56, "hello") # doctest: +ELLIPSIS <mocker.Mock object at ...> >>> mocker.result(11) >>> mocker.replay() >>> func(56, "hello") 11 >>> mocker.restore() >>> mocker.verify() Mocking containers Containers are another category of somewhat special objects that can be mocked. Like functions, containers can be mocked by simply using a mock object as if it were a container during your example. Mock objects are able to understand examples that involve the following container operations: looking up a member, setting a member, deleting a member, finding the length, and getting an iterator over the members. Depending on the version of Mocker, membership testing via the in operator may also be available. In the following example, all of the above capabilities are demonstrated, but the in tests are disabled for compatibility with versions of Mocker that don't support them. Keep in mind that even though, after we call replay, the object called container looks like an actual container, it's not. It's just responding to stimuli we told it to expect, in the way we told it to respond. That's why, when our test asks for an iterator, it returns None instead. That's what we told it to do, and that's all it knows. >>> from mocker import Mocker >>> mocker = Mocker() >>> container = mocker.mock() >>> container['hi'] = 18 >>> container['hi'] # doctest: +ELLIPSIS <mocker.Mock object at ...> >>> mocker.result(18) >>> len(container) 0 >>> mocker.result(1) >>> 'hi' in container # doctest: +SKIP True >>> mocker.result(True) >>> iter(container) # doctest: +ELLIPSIS <...> >>> mocker.result(None) >>> del container['hi'] >>> mocker.result(None) >>> mocker.replay() >>> container['hi'] = 18 >>> container['hi'] 18 >>> len(container) 1 >>> 'hi' in container # doctest: +SKIP True >>> for key in container: ... print key Traceback (most recent call last): TypeError: iter() returned non-iterator of type 'NoneType' >>> del container['hi'] >>> mocker.restore() >>> mocker.verify() Something to notice in the above example is that during the initial phase, a few of the demonstrations (for example, the call to len) did not return a mocker.Mock object, as we might have expected. For some operations, Python enforces that the result is of a particular type (for example, container lengths have to be integers), which forces Mocker to break its normal pattern. Instead of returning a generic mock object, it returns an object of the correct type, although the value of the returned object is meaningless. Fortunately, this only applies during the initial phase, when you're showing Mocker what to expect, and only in a few cases, so it's usually not a big deal. There are times when the returned mock objects are needed, though, so it's worth knowing about the exceptions.
Read more
  • 0
  • 0
  • 5567
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-granting-access-mysql-python
Packt
28 Dec 2010
10 min read
Save for later

Granting Access in MySQL for Python

Packt
28 Dec 2010
10 min read
MySQL for Python Integrate the flexibility of Python and the power of MySQL to boost the productivity of your Python applications Implement the outstanding features of Python's MySQL library to their full potential See how to make MySQL take the processing burden from your programs Learn how to employ Python with MySQL to power your websites and desktop applications Apply your knowledge of MySQL and Python to real-world problems instead of hypothetical scenarios A manual packed with step-by-step exercises to integrate your Python applications with the MySQL database server Introduction As with creating a user, granting access can be done by modifying the mysql tables directly. However, this method is error-prone and dangerous to the stability of the system and is, therefore, not recommended. Important dynamics of GRANTing access Where CREATE USER causes MySQL to add a user account, it does not specify that user's privileges. In order to grant a user privileges, the account of the user granting the privileges must meet two conditions: Be able to exercise those privileges in their account Have the GRANT OPTION privilege on their account Therefore, it is not just users who have a particular privilege or only users with the GRANT OPTION privilege who can authorize a particular privilege for a user, but only users who meet both requirements. Further, privileges that are granted do not take effect until the user's first login after the command is issued. Therefore, if the user is logged into the server at the time you grant access, the changes will not take effect immediately. The GRANT statement in MySQL The syntax of a GRANT statement is as follows: GRANT <privileges> ON <database>.<table> TO '<userid>'@'<hostname>'; Proceeding from the end of the statement, the userid and hostname follow the same pattern as with the CREATE USER statement. Therefore, if a user is created with a hostname specified as localhost and you grant access to that user with a hostname of '%', they will encounter a 1044 error stating access is denied. The database and table values must be specifi ed individually or collectively. This allows us to customize access to individual tables as necessary. For example, to specify access to the city table of the world database, we would use world.city. In many instances, however, you are likely to grant the same access to a user for all tables of a database. To do this, we use the universal quantifi er ('*'). So to specify all tables in the world database, we would use world.*. We can apply the asterisk to the database field as well. To specify all databases and all tables, we can use *.*. MySQL also recognizes the shorthand * for this. Finally, the privileges can be singular or a series of comma-separated values. If, for example, you want a user to only be able to read from a database, you would grant them only the SELECT privilege. For many users and applications, reading and writing is necessary but no ability to modify the database structure is warranted. In such cases, we can grant the user account both SELECT and INSERT privileges with SELECT, INSERT. To learn which privileges have been granted to the user account you are using, use the statement SHOW GRANTS FOR &ltuser>@hostname>;. With this in mind, if we wanted to grant a user tempo all access to all tables in the music database but only when accessing the server locally, we would use this statement: GRANT ALL PRIVILEGES ON music.* TO 'tempo'@'localhost'; Similarly, if we wanted to restrict access to reading and writing when logging in remotely, we would change the above statement to read: GRANT SELECT,INSERT ON music.* TO 'tempo'@'%'; If we wanted user conductor to have complete access to everything when logged in locally, we would use: GRANT ALL PRIVILEGES ON * TO 'conductor'@'localhost'; Building on the second example statement, we can further specify the exact privileges we want on the columns of a table by including the column numbers in parentheses after each privilege. Hence, if we want tempo to be able to read from columns 3 and 4 but only write to column 4 of the sheets table in the music database, we would use this command: GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%'; Note that specifying columnar privileges is only available when specifying a single database table—use of the asterisk as a universal quantifi er is not allowed. Further, this syntax is allowed only for three types of privileges: SELECT, INSERT, and UPDATE. A list of privileges that are available through MySQL is reflected in the following table: MySQL does not support the standard SQL UNDER privilege and does not support the use of TRIGGER until MySQL 5.1.6. More information on MySQL privileges can be found at http://dev.mysql.com/doc/refman/5.5/en/privileges-provided.html Using REQUIREments of access Using GRANT with a REQUIRE clause causes MySQL to use SSL encryption. The standard used by MySQL for SSL is the X.509 standard of the International Telecommunication Union's (ITU) Standardization Sector (ITU-T). It is a commonly used public-key encryption standard for single sign-on systems. Parts of the standard are no longer in force. You can read about the parts which still apply on the ITU website at http://www.itu.int/rec/T-REC-X.509/en The REQUIRE clause takes the following arguments with their respective meanings and follows the format of their respective examples: NONE: The user account has no requirement for an SSL connection. This is the default. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%'; SSL: The client must use an SSL-encrypted connection to log in. In most MySQL clients, this is satisfied by using the --ssl-ca option at the time of login. Specifying the key or certifi cate is optional. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' REQUIRE SSL; X509: The client must use SSL to login. Further, the certificate must be verifiable with one of the CA vendors. This option further requires the client to use the --ssl-ca option as well as specifying the key and certificate using --ssl-key and --ssl-cert, respectively. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' REQUIRE X509; CIPHER: Specifies the type and order of ciphers to be used. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' REQUIRE CIPHER 'RSA-EDH-CBC3-DES-SHA'; ISSUER: Specifies the issuer from whom the certificate used by the client is to come. The user will not be able to login without a certificate from that issuer. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' REQUIRE ISSUER 'C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division,CN=Thawte Server CA/emailAddress=server-certs@thawte. com'; SUBJECT: Specifies the subject contained in the certificate that is valid for that user. The use of a certificate containing any other subject is disallowed. GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' REQUIRE SUBJECT 'C=US, ST=California, L=Pasadena, O=Indiana Grones, OU=Raiders, CN=www.lostarks.com/ emailAddress=indy@lostarks.com'; Using a WITH clause MySQL's WITH clause is helpful in limiting the resources assigned to a user. WITH takes the following options: GRANT OPTION: Allows the user to provide other users of any privilege that they have been granted MAX_QUERIES_PER_HOUR: Caps the number of queries that the account is allowed to request in one hour MAX_UPDATES_PER_HOUR: Limits how frequently the user is allowed to issue UPDATE statements to the database MAX_CONNECTIONS_PER_HOUR: Limits the number of logins that a user is allowed to make in one hour MAX_USER_CONNECTIONS: Caps the number of simultaneous connections that the user can make at one time It is important to note that the GRANT OPTION argument to WITH has a timeless aspect. It does not statically apply to the privileges that the user has just at the time of issuance, but if left in effect, applies to any options the user has at any point in time. So, if the user is granted the GRANT OPTION for a temporary period, but the option is never removed, then the user grows in responsibilities and privileges, that user can grant those privileges to any other user. Therefore, one must remove the GRANT OPTION when it is not longer appropriate. Note also that if a user with access to a particular MySQL database has the ALTER privilege and is then granted the GRANT OPTION privilege, that user can then grant ALTER privileges to a user who has access to the mysql database, thus circumventing the administrative privileges otherwise needed. The WITH clause follows all other options given in a GRANT statement. So, to grant user tempo the GRANT OPTION, we would use the following statement: GRANT SELECT (col3,col4),INSERT (col4) ON music.sheets TO 'tempo'@'%' WITH GRANT OPTION; If we want to limit the number of queries that the user can have in one hour to five, as well, we simply add to the argument of the single WITH statement. We do not need to use WITH a second time. GRANT SELECT,INSERT ON music.sheets TO 'tempo'@'%' WITH GRANT OPTION MAX_QUERIES_PER_HOUR 5; More information on the many uses of WITH can be found at http://dev.mysql.com/doc/refman/5.1/en/grant.html Granting access in Python Using MySQLdb to enable user privileges is not more difficult than doing so in MySQL itself. As with creating and dropping users, we simply need to form the statement and pass it to MySQL through the appropriate cursor. As with the native interface to MySQL, we only have as much authority in Python as our login allows. Therefore, if the credentials with which a cursor is created has not been given the GRANT option, an error will be thrown by MySQL and MySQLdb, subsequently. Assuming that user skipper has the GRANT option as well as the other necessary privileges, we can use the following code to create a new user, set that user's password, and grant that user privileges: #!/usr/bin/env python import MySQLdb host = 'localhost' user = 'skipper' passwd = 'secret' mydb = MySQLdb.connect(host, user, passwd) cursor = mydb.cursor() try: mkuser = 'symphony' creation = "CREATE USER %s@'%s'" %(mkuser, host) results = cursor.execute(creation) print "User creation returned", results mkpass = 'n0n3wp4ss' setpass = "SET PASSWORD FOR '%s'@'%s' = PASSWORD('%s')" %(mkuser, host, mkpass) results = cursor.execute(setpass) print "Setting of password returned", results granting = "GRANT ALL ON *.* TO '%s'@'%s'" %(mkuser, host) results = cursor.execute(granting) print "Granting of privileges returned", results except MySQLdb.Error, e: print e If there is an error anywhere along the way, it is printed to screen. Otherwise, the several print statements are executed. As long as they all return 0, each step was successful.
Read more
  • 0
  • 0
  • 12651

article-image-getting-started-ext-gwt
Packt
28 Dec 2010
8 min read
Save for later

Getting Started with Ext GWT

Packt
28 Dec 2010
8 min read
  Ext GWT 2.0: Beginner's Guide Take the user experience of your website to a new level with Ext GWT Explore the full range of features of the Ext GWT library through practical, step-by-step examples Discover how to combine simple building blocks into powerful components Create powerful Rich Internet Applications with features normally only found in desktop applications Learn how to structure applications using MVC for maximum reliability and maintainability      What is GWT missing? GWT is a toolkit as opposed to a full development framework, and for most projects, it forms the part of a solution rather than the whole solution. Out-of-the-box GWT comes with only a basic set of widgets and lacks a framework to enable the developers to structure larger applications. Fortunately, GWT is both open and extensible and as a result, a range of complementary projects have grown up around it. Ext GWT is one of those projects. What does Ext GWT offer? Ext GWT sets out to build upon the strengths of GWT by enabling the developers to give their users an experience more akin to that of a desktop application. Ext GWT provides the GWT developer with a comprehensive component library similar to that used when developing for desktop environments. In addition to being a component library, powerful features for working with local and remote data are provided. It also features a model view controller framework, which can be used to structure larger applications. How is Ext GWT licensed? Licensing is always an important consideration when choosing technology to use in a project. At the time of writing, Ext GWT is offered with a dual license. The first license is an open source license compatible with the GNU GPL license v3. If you wish to use this license, you do not have to pay a fee for using Ext GWT, but in return you have to make your source code available under an open source license. This means you have to contribute all the source code of your project to the open source community and give everyone the right to modify or redistribute it. If you cannot meet the obligations of the open source license, for example, you are producing a commercial product or simply do not want to share your source code, you have to purchase a commercial license for Ext GWT. It is a good idea to check the current licensing requirements on the Sencha website, http://www.sencha.com, and take that into account when planning your project. Alternatives to Ext GWT Ext GWT is one of the many products produced by the company Sencha. Sencha was previously named Ext JS and started off developing a JavaScript library by the same name. Ext GWT is closely related to the Ext JS product in terms of functionality. Both Ext GWT and Ext JS also share the same look and feel as well as a similar API structure. However, Ext GWT is a native GWT implementation, written almost entirely in Java rather than a wrapper, the JavaScript-based Ext JS. GWT-Ext Before Ext GWT, there was GWT-Ext: http://code.google.com/p/gwt-ext/. This library was developed by Sanjiv Jeevan as a GWT wrapper around an earlier, 2.0.2 version of Ext JS. Being based on Ext JS, it has a very similar look and feel to Ext GWT. However, after the license of Ext JS changed from LGPL to GPL in 2008, active development came to an end. Apart from no longer being developed or supported, developing with GWT-Ext is more difficult than with Ext GWT. This is because the library is a wrapper around JavaScript and the Java debugger cannot help when there is a problem in the JavaScript code. Manual debugging is required. Smart GWT When development of GWT-Ext came to an end, Sanjiv Jeevan started a new project named Smart GWT: http://www.smartclient.com/smartgwt/. This is a LGPL framework that wraps the Smart Client JavaScript library in a similar way that GWT-Ext wraps Ext JS. Smart GWT has the advantage that it is still being actively developed. Being LGPL-licensed, it also can be used commercially without the need to pay the license fee that is required for Ext GWT. Smart GWT still has the debugging problems of GWT-Ext and the components are often regarded not as visually pleasing as Ext GWT. This could be down to personal taste of course. Vaadin Vaadin, http://vaadin.com, is a third alternative to Ext GWT. Vaadin is a server-side framework that uses a set of precompiled GWT components. Although you can write your own components if required, Vaadin is really designed so that you can build applications by combining the ready-made components. In Vaadin the browser client is just a dumb view of the server components and any user interaction is sent to the server for processing much like traditional Java web frameworks. This can be slow depending on the speed of the connection between the client and the server. The main disadvantage of Vaadin is the dependency on the server. GWT or Ext GWT's JavaScript can run in a browser without needing to communicate with a server. This is not possible in Vaadin. Ext GWT or GXT? To avoid confusion with GWT-Ext and to make it easier to write, Ext GWT is commonly abbreviated to GXT. We will use GXT synonymously with Ext GWT throughout the rest of this article. Working with GXT: A different type of web development If you are a web developer coming to GXT or GWT for the first time, it is very important to realize that working with this toolset is not like traditional web development. In traditional web development, most of the work is done on the server and the part the browser plays is li?? le more than a view-making request and receiving responses. When using GWT, especially GXT, at times it is easier if you suspend your web development thinking and think more like a desktop-rich client developer. Java Swing developers, for example, may find themselves at home. How GXT fits into GWT GXT is simply a library that plugs into any GWT project. If we have an existing GWT project setup, all we need to do to use it is: Download the GXT SDK from the Sencha website Add the library to the project and reference it in the GWT configuration Copy a set of resource files to the project If you haven't got a GWT project setup, don't worry. We will now work through getting GXT running from the beginning. Downloading what you need Before we can start working with GXT, we first need to download the toolkit and set up our development environment. Here is the list of what you need to download for running the examples.     Recommended Notes Download from Sun JDK 6 The Java development kit http://java.sun.com/javase/downloads/widget/jdk6.jsp Eclipse IDE for Java EE Developers 3.6 The Eclipse IDE for Java developers, which also includes some useful web development tools http://www.eclipse.org/downloads/ Ext GWT 2.2.0 SDK for GWT 2.0 The GXT SDK itself http://www.sencha.com/products/gwt/download.php Google supplies a useful plugin that integrates GWT into Eclipse. However, there is no reason that you cannot use an alternative development environment, if you prefer. Eclipse setup There are different versions of Eclipse, and although Eclipse for Java EE developers is not strictly required, it contains some useful tools for editing web-specific files such as CSS. These tools will be useful for GXT development, so it is strongly recommended. We will not cover the details of installing Eclipse here, as this is covered more than adequately on the Eclipse website. For that reason, we make the assumption that you already have a fresh installation of Eclipse ready to go. GWT setup You may have noticed that GWT is not included in the list of downloads. This is because since version 2.0.0, GWT has been available within an Eclipse plugin, which we will now set up. Time for action – setting up GWT In Eclipse, select Help Install New Software|. The installation dialog will appear. Click on the Add button to add a new site. Enter the name and location in the respective fields, as shown in the following screenshot, and click on the OK button. Move the mouse over the image to enlarge it. Select Google Plugin for Eclipse from the plugin section and Google Web Toolkit SDK from the SDKs section. Click on Next. The following dialog will appear. Click on Next to proceed. Click on the radio button to accept the license. Click on Finish. Eclipse will now download the Google Web Toolkit and configure the plugin. Restart when prompted. On restarting, if GWT and the Google Eclipse Plugin are installed successfully, you will notice the following three new icons in your toolbar. What just happened? You have now set up GWT in your Eclipse IDE. You are now ready to create GWT applications. However, before we can create GXT applications, there is a bit more work to do.
Read more
  • 0
  • 0
  • 5482

article-image-getting-and-running-mysql-python
Packt
24 Dec 2010
8 min read
Save for later

Getting Up and Running with MySQL for Python

Packt
24 Dec 2010
8 min read
  MySQL for Python Integrate the flexibility of Python and the power of MySQL to boost the productivity of your Python applications Implement the outstanding features of Python's MySQL library to their full potential See how to make MySQL take the processing burden from your programs Learn how to employ Python with MySQL to power your websites and desktop applications Apply your knowledge of MySQL and Python to real-world problems instead of hypothetical scenarios A manual packed with step-by-step exercises to integrate your Python applications with the MySQL database server Getting MySQL for Python How you get MySQL for Python depends on your operating system and the level of authorization you have on it. In the following subsections, we walk through the common operating systems and see how to get MySQL for Python on each. Using a package manager (only on Linux) Package managers are used regularly on Linux, but none come by default with Macintosh and Windows installations. So users of those systems can skip this section. A package manager takes care of downloading, unpacking, installing, and configuring new software for you. In order to use one to install software on your Linux installation, you will need administrative privileges. Administrative privileges on a Linux system can be obtained legitimately in one of the following three ways: Log into the system as the root user (not recommended) Switch user to the root user using su Use sudo to execute a single command as the root user The first two require knowledge of the root user's password. Logging into a system directly as the root user is not recommended due to the fact that there is no indication in the system logs as to who used the root account. Logging in as a normal user and then switching to root using su is better because it keeps an account of who did what on the machine and when. Either way, if you access the root account, you must be very careful because small mistakes can have major consequences. Unlike other operating systems, Linux assumes that you know what you are doing if you access the root account and will not stop you from going so far as deleting every file on the hard drive. Unless you are familiar with Linux system administration, it is far better, safer, and more secure to prefix the sudo command to the package manager call. This will give you the benefit of restricting use of administrator-level authority to a single command. The chances of catastrophic mistakes are therefore mitigated to a great degree. More information on any of these commands is available by prefacing either man or info before any of the preceding commands (su, sudo). Which package manager you use depends on which of the two mainstream package management systems your distribution uses. Users of RedHat or Fedora, SUSE, or Mandriva will use the RPM Package Manager (RPM) system. Users of Debian, Ubuntu, and other Debian-derivatives will use the apt suite of tools available for Debian installations. Each package is discussed in the following: Using RPMs and yum If you use SUSE, RedHat, or Fedora, the operating system comes with the yum package manager . You can see if MySQLdb is known to the system by running a search (here using sudo): sudo yum search mysqldb If yum returns a hit, you can then install MySQL for Python with the following command: sudo yum install mysqldb Using RPMs and urpm If you use Mandriva, you will need to use the urpm package manager in a similar fashion. To search use urpmq: sudo urpmq mysqldb And to install use urpmi: sudo urpmi mysqldb Using apt tools on Debian-like systems Whether you run a version of Ubuntu, Xandros, or Debian, you will have access to aptitude, the default Debian package manager. Using sudo we can search for MySQLdb in the apt sources using the following command: sudo aptitude search mysqldb On most Debian-based distributions, MySQL for Python is listed as python-mysqldb. Once you have found how apt references MySQL for Python, you can install it using the following code: sudo aptitude install python-mysqldb Using a package manager automates the entire process so you can move to the section Importing MySQL for Python. Using an installer for Windows Windows users will need to use the older 1.2.2 version of MySQL for Python. Using a web browser, go to the following link: http://sourceforge.net/projects/mysql-python/files/ This page offers a listing of all available files for all platforms. At the end of the file listing, find mysql-python and click on it. The listing will unfold to show folders containing versions of MySQL for Python back to 0.9.1. The version we want is 1.2.2. Windows binaries do not currently exist for the 1.2.3 version of MySQL for Python. To get them, you would need to install a C compiler on your Windows installation and compile the binary from source. Click on 1.2.2 and unfold the file listing. As you will see, the Windows binaries are differentiated by Python version—both 2.4 and 2.5 are supported. Choose the one that matches your Python installation and download it. Note that all available binaries are for 32-bit Windows installations, not 64-bit. After downloading the binary, installation is a simple matter of double-clicking the installation EXE file and following the dialogue. Once the installation is complete, the module is ready for use. So go to the section Importing MySQL for Python. Using an egg file One of the easiest ways to obtain MySQL for Python is as an egg file, and it is best to use one of those files if you can. Several advantages can be gained from working with egg files such as: They can include metadata about the package, including its dependencies They allow for the use of egg-aware software, a helpful level of abstraction Eggs can, technically, be placed on the Python executable path and used without unpacking They save the user from installing packages for which they do not have the appropriate version of software They are so portable that they can be used to extend the functionality of third-party applications Installing egg handling software One of the best known egg utilities—Easy Install, is available from the PEAK Developers' Center at http://peak.telecommunity.com/DevCenter/EasyInstall. How you install it depends on your operating system and whether you have package management software available. In the following section, we look at several ways to install Easy Install on the most common systems. Using a package manager (Linux) On Ubuntu you can try the following to install the easy_install tool (if not available already): shell> sudo aptitude install python-setuptools On RedHat or CentOS you can try using the yum package manager: shell> sudo yum install python-setuptools On Mandriva use urpmi: shell> sudo urpmi python-setuptools You must have administrator privileges to do the installations just mentioned. Without a package manager (Mac, Linux) If you do not have access to a Linux package manager, but nonetheless have a Unix variant as your operating system (for example, Mac OS X), you can install Python's setuptools manually. Go to: http://pypi.python.org/pypi/setuptools#files Download the relevant egg file for your Python version. When the file is downloaded, open a terminal and change to the download directory. From there you can run the egg file as a shell script. For Python 2.5, the command would look like this: sh setuptools-0.6c11-py2.5.egg This will install several files, but the most important one for our purposes is easy_install, usually located in /usr/bin. On Microsoft Windows On Windows, one can download the setuptools suite from the following URL: http://pypi.python.org/pypi/setuptools#files From the list located there, select the most appropriate Windows executable file. Once the download is completed, double-click the installation file and proceed through the dialogue. The installation process will set up several programs, but the one important for our purposes is easy_install.exe. Where this is located will differ by installation and may require using the search function from the Start Menu. On 64-bit Windows, for example, it may be in the Program Files (x86) directory. If in doubt, do a search. On Windows XP with Python 2.5, it is located here: C:Python25Scriptseasy_install.exe Note that you may need administrator privileges to perform this installation. Otherwise, you will need to install the software for your own use. Depending on the setup of your system, this may not always work. Installing software on Windows for your own use requires the following steps: Copy the setuptools installation file to your Desktop. Right-click on it and choose the runas option. Enter the name of the user who has enough rights to install it (presumably yourself) After the software has been installed, ensure that you know the location of the easy_install.exe file. You will need it to install MySQL for Python.
Read more
  • 0
  • 0
  • 3200

article-image-python-built-functions
Packt
24 Dec 2010
10 min read
Save for later

Python Built-in Functions

Packt
24 Dec 2010
10 min read
  Python 3 Object Oriented Programming Harness the power of Python 3 objects Learn how to do Object Oriented Programming in Python using this step-by-step tutorial Design public interfaces using abstraction, encapsulation, and information hiding Turn your designs into working software by studying the Python syntax Raise, handle, define, and manipulate exceptions using special error objects Implement Object Oriented Programming in Python using practical examples         Read more about this book       (For more resources on Python, see here.) There are numerous functions in Python that perform a task or calculate a result on certain objects without being methods on the class. Their purpose is to abstract common calculations that apply to many types of classes. This is applied duck typing; these functions accept objects with certain attributes or methods that satisfy a given interface, and are able to perform generic tasks on the object. Len The simplest example is the len() function. This function counts the number of items in some kind of container object such as a dictionary or list. For example: >>> len([1,2,3,4])4 Why don't these objects have a length property instead of having to call a function on them? Technically, they do. Most objects that len() will apply to have a method called __len__() that returns the same value. So len(myobj) seems to callmyobj.__len__(). Why should we use the function instead of the method? Obviously the method is a special method with double-underscores suggesting that we shouldn't call it directly. There must be an explanation for this. The Python developers don't make such design decisions lightly. The main reason is efficiency. When we call __len__ on an object, the object has to look the method up in its namespace, and, if the special __getattribute__ method (which is called every time an attribute or method on an object is accessed) is defined on that object, it has to be called as well. Further __getattribute__ for that particular method may have been written to do something nasty like refusing to give us access to special methods such as __len__! The len function doesn't encounter any of this. It actually calls the __len__ function on the underlying class, so len(myobj) maps to MyObj.__len__(myobj). Another reason is maintainability. In the future, the Python developers may want to change len() so that it can calculate the length of objects that don't have a __len__, for example by counting the number of items returned in an iterator. They'll only have to change one function instead of countless __len__ methods across the board. Reversed The reversed() function takes any sequence as input, and returns a copy of that sequence in reverse order. It is normally used in for loops when we want to loop over items from back to front. Similar to len, reversed calls the __reversed__() function on the class for the parameter. If that method does not exist, reversed builds the reversed sequence itself using calls to __len__ and __getitem__. We only need to override __reversed__ if we want to somehow customize or optimize the process: normal_list=[1,2,3,4,5]class CustomSequence(): def __len__(self): return 5 def __getitem__(self, index): return "x{0}".format(index)class FunkyBackwards(CustomSequence): def __reversed__(self): return "BACKWARDS!"for seq in normal_list, CustomSequence(), FunkyBackwards(): print("n{}: ".format(seq.__class__.__name__), end="") for item in reversed(seq): print(item, end=", ") The for loops at the end print the reversed versions of a normal list, and instances of the two custom sequences. The output shows that reversed works on all three of them, but has very different results when we define __reversed__ ourselves: list: 5, 4, 3, 2, 1,CustomSequence: x4, x3, x2, x1, x0,FunkyBackwards: B, A, C, K, W, A, R, D, S, !, Note: the above two classes aren't very good sequences, as they don't define a proper version of __iter__ so a forward for loop over them will never end. Enumerate Sometimes when we're looping over an iterable object in a for loop, we want access to the index (the current position in the list) of the current item being processed. The for loop doesn't provide us with indexes, but the enumerate function gives us something better: it creates a list of tuples, where the first object in each tuple is the index and the second is the original item. This is useful if we want to use index numbers directly. Consider some simple code that outputs all the lines in a file with line numbers: import sysfilename = sys.argv[1]with open(filename) as file: for index, line in enumerate(file): print("{0}: {1}".format(index+1, line), end='') Running this code on itself as the input file shows how it works: 1: import sys2: filename = sys.argv[1]3:4: with open(filename) as file:5: for index, line in enumerate(file):6: print("{0}: {1}".format(index+1, line), end='') The enumerate function returns a list of tuples, our for loop splits each tuple into two values, and the print statement formats them together. It adds one to the index for each line number, since enumerate, like all sequences is zero based. Zip The zip function is one of the least object-oriented functions in Python's collection. It takes two or more sequences and creates a new sequence of tuples. Each tuple contains one element from each list. This is easily explained by an example; let's look at parsing a text file. Text data is often stored in tab-delimited format, with a "header" row as the first line in the file, and each line below it describing data for a unique record. A simple contact list in tab-delimited format might look like this: first last emailjohn smith jsmith@example.comjane doan janed@example.comdavid neilson dn@example.com A simple parser for this file can use zip to create lists of tuples that map headers to values. These lists can be used to create a dictionary, a much easier object to work with in Python than a file! import sysfilename = sys.argv[1]contacts = []with open(filename) as file: header = file.readline().strip().split('t') for line in file: line = line.strip().split('t') contact_map = zip(header, line) contacts.append(dict(contact_map))for contact in contacts: print("email: {email} -- {last}, {first}".format( **contact)) What's actually happening here? First we open the file, whose name is provided on the command line, and read the first line. We strip the trailing newline, and split what's left into a list of three elements. We pass 't' into the strip method to indicate that the string should be split at tab characters. The resulting header list looks like ["first", "last", "email"]. Next, we loop over the remaining lines in the file (after the header). We split each line into three elements. Then, we use zip to create a sequence of tuples for each line. The first sequence would look like [("first", "john"), ("last", "smith"), ("email", "jsmith@example.com")]. Pay attention to what zip is doing. The first list contains headers; the second contains values. The zip function created a tuple of header/value pairs for each matchup. The dict constructor takes the list of tuples, and maps the first element to a key and the second to a value to create a dictionary. The result is added to a list. At this point, we are free to use dictionaries to do all sorts of contact-related activities. For testing, we simply loop over the contacts and output them in a different format. The format line, as usual, takes variable arguments and keyword arguments. The use of **contact automatically converts the dictionary to a bunch of keyword arguments (we'll understand this syntax before the end of the chapter) Here's the output: email: jsmith@example.com -- smith, johnemail: janed@example.com -- doan, janeemail: dn@example.com -- neilson, david If we provide zip with lists of different lengths, it will stop at the end of the shortest list. There aren't many useful applications of this feature, but zip will not raise an exception if that is the case. We can always check the list lengths and add empty values to the shorter list, if necessary. The zip function is actually the inverse of itself. It can take multiple sequences and combine them into a single sequence of tuples. Because tuples are also sequences, we can "unzip" a zipped list of tuples by zipping it again. Huh? Have a look at this example: >>> list_one = ['a', 'b', 'c']>>> list_two = [1, 2, 3]>>> zipped = zip(list_one, list_two)>>> zipped = list(zipped)>>> zipped[('a', 1), ('b', 2), ('c', 3)]>>> unzipped = zip(*zipped)>>> list(unzipped)[('a', 'b', 'c'), (1, 2, 3)] First we zip the two lists and convert the result into a list of tuples. We can then use parameter unpacking to pass these individual sequences as arguments to the zip function. zip matches the first value in each tuple into one sequence and the second value into a second sequence; the result is the same two sequences we started with! Other functions Another key function is sorted(), which takes an iterable as input, and returns a list of the items in sorted order. It is very similar to the sort() method on lists, the difference being that it works on all iterables, not just lists. Like list.sort, sorted accepts a key argument that allows us to provide a function to return a sort value for each input. It can also accept a reverse argument. Three more functions that operate on sequences are min, max, and sum. These each take a sequence as input, and return the minimum or maximum value, or the sum of all values in the sequence. Naturally, sum only works if all values in the sequence are numbers. The max and min functions use the same kind of comparison mechanism as sorted and list.sort, and allow us to define a similar key function. For example, the following code uses enumerate, max, and min to return the indices of the values in a list with the maximum and minimum value: def min_max_indexes(seq): minimum = min(enumerate(seq), key=lambda s: s[1]) maximum = max(enumerate(seq), key=lambda s: s[1]) return minimum[0], maximum[0] The enumerate call converts the sequence into (index, item) tuples. The lambda function passed in as a key tells the function to search the second item in each tuple (the original item). The minimum and maximum variables are then set to the appropriate tuples returned by enumerate. The return statement takes the first value (the index from enumerate) of each tuple and returns the pair. The following interactive session shows how the returned values are, indeed, the indices of the minimum and maximum values: >>> alist = [5,0,1,4,6,3]>>> min_max_indexes(alist)(1, 4)>>> alist[1], alist[4](0, 6) We've only touched on a few of the more important Python built-in functions. There are numerous others in the standard library, including: all and any, which accept an iterable and returns True if all, or any, of the items evaluate to true (that is a non-empty string or list, a non-zero number, an object that is not None, or the literal True). eval, exec, and compile, which execute string as code inside the interpreter. hasattr, getattr, setattr, and delattr, which allow attributes on an object to be manipulated as string names. And many more! See the interpreter help documentation for each of the functions listed in dir(__builtins__). Summary In this article we took a look at many useful built-in functions. Further resources on this subject: Python Graphics: Animation Principles [Article] Animating Graphic Objects using Python [Article] Python 3: When to Use Object-oriented Programming [Article] Objects in Python [Article]
Read more
  • 0
  • 0
  • 6485
article-image-using-urls-access-nnmi-objects
Packt
24 Dec 2010
14 min read
Save for later

Using URLs to Access NNMi Objects

Packt
24 Dec 2010
14 min read
  HP Network Node Manager 9: Getting Started Manage your network effectively with NNMi Install, customize, and expand NNMi functionality by developing custom features Integrate NNMi with other management tools, such as HP SW Operations Manager, Network Automation, Cisco Works, Business Availability center, UCMDB, and many others Navigate between incidents and maps to reduce troubleshooting time Screenshots and step-by-step instructions to customize NNMi in the way you want Instructions in the book are valid for version 8 as well         Read more about this book       URLs that are listed in this section can be grouped into a few groups (refer to the preceding screenshot): Generic URLs. Workspace-related URLs. Form-related URLs. Menu item-related URLs. As an example, we can provide a quick access to menu items for our team. For example, building a smart-phone application that displays critical events and allows user to run ping, trace route, or other commands for faster troubleshooting (refer to the next diagram). The following section lists these URLs and provides tables of parameters, which can be used for building URLs. Note: If our NNMi is configured to use the https protocol, we need to modify our URLs in this article accordingly, as URLs here are provided for http access. Generic URLs Generic URLs that can be used for obtaining information about NNMi are: View launching URL: To launch the NNMi console, use the following URL:http://<server_name>:<port_number>/nnm/launch?cmd=showMain. If we are passing by using a username and password within the URL, we need to use the following syntax: http://<server_name>:<port_number>/nnm/launch?cmd=showMain&j_username=<account_name>&j_password=<account_password>. To confirm whether NNMi is running: The following URL should be used: http:/<serverName>:<portNumber>/nnm/launch?cmd=isRunning. Using this URL, we will receive a confirmation response on whether or not NNMi is running. Otherwise, a browser error message will be sent saying that the URL is unreachable. Workspace-related URLs This URL group is related to NNMi views. Here is a list of available URLs. Launching view The following table presents the required attributes and their values for launching views. Attributes Values Description Use following URL to show general view: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&objtype=<x> objtype Incident Node Interface IP Address IP Subnet NodeGroup InterfaceGroup Incidents workspace, All Incidents table view. Inventory workspace, Nodes table view. Inventory workspace, Interfaces table view. Inventory workspace, IP Addresses table view. Inventory workspace, IP Subnets table view. Inventory workspace, Node Groups table view. Inventory workspace, Interface Groups table view. Use the following URL to show general view for a specific node group, filtered by node group name, id, or uuid: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&objtype= <x>&nodegroup=<Name> nodegroup nodegroup nodegroupid     nodegroupuuid The Name attribute value of Node Group*. The ID number of node group. This ID is unique across all NNMi database. Use nnmconfigexport.ovpl tool to see ID number of node group. Universally Unique Object ID of the node group. Use nnmconfigexport.ovpl tool to see ID number of node group. Use following URL to show general view for a specific node group, filtered by interface group name or id: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&objtype=<x>&ifgroup=<Name> ifgroup ifgroup ifgroupid     ifgroupduuid The name attribute value of interface group*. The ID number of interface group. This ID is unique across all NNMi database. Use nnmconfigexport.ovpl tool to see id number of interface group. Universally Unique Object ID of the interface group. Use nnmconfigexport.ovpl tool to see id number of interface group. Use following URL to show general view with custom window settings: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&objtype=<x>&menus=<true|false>&newWindow=<true|false>&envattrs=<name1=value>;<name2=value> menus [true|false] True: Show the view menus. False: Hide the view menus. The default value is true. newWindow [true|false] True: To display view in new window. False: To display view within same browser window. Default value is false. envattrs   This is session-specific attributes and they are stored by following syntax: <name=value>.     * If the node group name has spaces in it, use one of the following symbols to represent a "space" character (without brackets): "%20", "+", or " " (space symbol). Consider the following example. To open a window with incidents, which are related to the node group Europe, run the following URL (assuming, that NNMi is installed on node "box1" and uses port number "8080"): http://box1:8080/nnm/launch?cmd=showView&objtype=Incident&nodegroup=Europe Launching an Incident view The following table presents the required attributes and their values for launching incident-specific views. Attributes Values Description Use following URL to show incident view, filtered by specific incident attributes: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x> view allIncidentsTableView allOpenIncidentsTableView closedKeyIncidentsTableView closedRCIncidentTableView customIncidentTableView incidentsByNatureTableView incidentsByFamilyTableView keyIncidentsByLifecycleStateTableView myIncidentTableView nnm6x7xIncidentTableView nnm6x7xIncidentByCategoryTableView openKeyIncidentsTableView openKeyIncidentsByCategoryTableView openKeyIncidentsByFamilyTableView openKeyIncidentsByPriorityTableView openKeyIncidentsBySeverityTableView openRCIncidentsByCategoryTableView openRCIncidentsByFamilyTableView openRCIncidentsByPriorityTableView openRCIncidentsBySeverityTableView openRCIncidentTableView allRCIncidentTableView RCIncidentsByLifecycleStateTableView serviceImpactIncidentTableView snmpTrapsIncidentTableView snmpTrapsIncidentByFamilyTableView streamCorrelationIncidentTableView unassignedKeyIncidentsTableView unassignedIncidentTableView All incidents All open incidents Closed Key Incidents Closed Root Cause Incidents Custom Incidents Incidents by Correlation Nature Incidents by Family Key Incidents by Lifecycle State My Open Incidents NNM 6.x / 7.x Events NNM 6.x/7.x Event by Category Open Key Incidents Open Key Incidents by Category Open Key Incidents by Family Open Key Incidents by Priority Open Key Incidents by Severity Open Root Cause by Category Open Root Cause by Family Open Root Cause by Priority Open Root Cause by Severity Open Root Cause Incidents Root Cause Incidents Root Cause by Lifecycle State Service Impact Incidents SNMP Traps SNMP Traps by Family Stream Correlation Incidents Unassigned Open Key Incidents Unassigned Root Cause Incidents Use following URL to show Incident view, filtered for specific node group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&nodegroup_var=<Name> See the preceding table for attribute description. Use following URL to show Incident view with custom window settings: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&menus_var=<true|false>&newWindow=<true|false>&envattrs=<name1=value>;<name2=value> See the preceding table for attribute description.   Launching a Topology Maps Workspace view Here is a list of Topology Maps that can be opened using the following URLs: Consider the following example. Here are the links to open a few groups on NNMi server "box1" (port number 8080): Open Routers node groups: http://box1:8080/nnm/launch?cmd=showNodeGroup&name=Routers Open Networking Infrastructure devices: http://box1:8080/nnm/launch?cmd=showView&objtype= Node&nodegroup=Networking%20Infrastructure%20Devices Launching a Monitoring Workspace view The following table presents the required attributes and their values for launching Monitoring workspace-specific views. Attributes Values Description Use following URL to show monitoring view, filtered by specific attributes: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x> view criticalComponentHealthTableView criticalInterfaceTableView criticalNodeTableView nonNormalInterfaceTableView nonNormalNodeTableView notRespondingIPAddressTableView nodesByStatusTableView componentHealthByStatusTableView interfacesByStatusTableView interfacesByAdministrativeStateTableView interfacesByOperationalStateTableView IPAddressesByStateTableView interfacePerformanceTableView routerRedundancyGroupsTableView nodeGroupsStatusTableView Critical Component Health Critical Interfaces Critical Nodes Non-Normal Interfaces Non-Normal Nodes Not Responding Addresses Nodes by Status Component Health by Status Interfaces by Status Interfaces by Administrative State Interfaces by Operational State IP Addresses by State Interface Performance Router Redundancy Groups Node Groups Use following URL to show monitoring view, filtered by node group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&nodegroup=<Name> Refer to the table under the Launching view section for attribute description. Use following URL to show general view, filtered by interface group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&ifgroup=<Name> Refer to the table under the Launching view section for attribute description. Use following URL to show monitoring view with custom window settings: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&menus=<true|false>&newWindow=<true|false>&envattrs=<name1=value>;<name2=value> Refer to the table under the Launching view section for attribute description.   Launching a Troubleshooting Workspace view Troubleshooting workspace contains the following four different views: Layer 2 Neighbor view Layer 3 Neighbor view Path view Node Group Map view Use the following URL to show the Layer 2 Neighbor view: http://<serverName>:<portNumber>/nnm/launch?cmd=showLayer2Neighbors. Attributes   Values Description Use following URL to show Layer 2 Neighbor view by specified number of hops: http://<serverName>:<portNumber>/nnm/launch?cmd=showLayer2Neighbors&nodename=<x>&hops=<#> nodename nodename The source node's name or IP address hops 1 - 9 Number of hops Use following URL to show Layer 2 Neighbor view with custom window parameters: http://<serverName>:<portNumber>/nnm/launch?cmd=showLayer2Neighbors&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value> Refer to the table under the Launching view section for attribute description.   Use the following URL to show Layer 3 Neighbor view: http://<serverName>:<portNumber>/nnm/launch?cmd= showLayer3Neighbors. Attributes Values Description Use following URL to show Layer 3 Neighbor view for specified node by specified number of hops: http://<serverName>:<portNumber>/nnm/launch?cmd=showLayer3Neighbors&nodename=<x>&hops=<#> nodename See the previous table for attribute description. hops See the previous table for attribute description. menus [true|false] Show (true) or hide (false) menu and window toolbar. Default value is "true".   Use the following URL to show Layer 3 Neighbor view with the specified window parameters: http://<serverName>:<portNumber>/nnm/launch?cmd=showLayer3Neighbors&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value>. Use the following URL to show Path view: http://<serverName>:<portNumber>/nnm/launch?cmd=showPath. Attributes Values Description Use the following URL to show Path view by specified source and destination nodes: http://<serverName>:<portNumber>/nnm/launch?cmd=showPath&src=<x>&dest=<y> src src Source node's hostname or IPv4 address. dest dest Destination node's hostname or IPv4 address. Use the following URL to show Path view with custom window parameters: http://<serverName>:<portNumber>/nnm/launch?cmd=showPath&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2= value> Attributes Values Description   Use following URL to list nodes belonging to a specified node group: http://<serverName>:<portNumber>/nnm/launch?cmd=showNodeGroup&name=<x> name name The Name attribute value from the Node Group form   Use the following URL to list nodes belonging to a specified node group with custom window parameters: http://<serverName>:<portNumber>/nnm/launch?cmd=showNodeGroup&name=<x>&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value>. Launching an Inventory Workspace view The following table presents the required attributes and their values for launching Inventory workspace-specific views. Attributes Values Description Use the following URL to show a view, specified in the view list below in this table: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x> view allNodesTableView allInterfacesTableView allIPAddressTableView allIPSubnetsTableView allVlansTableView allLayer2ConnectionsTableView nodesByDeviceCategoryTableView interfacesByIfTypeTableView customNodeTableView customInterfaceTableView customIPAddressTableView routerRedundancyGroupsTableView nodeGroupsTableView interfaceGroupsTableView allManagementStationsTableView Nodes Interfaces IP Addresses IP Subnets VLANs Layer 2 Connections Nodes by Device Category Interfaces by IfType Custom Nodes Custom Interfaces Custom IP Addresses Router Redundancy Groups Node Groups Interface Groups Management Stations Use the following URL to show a view, specified in the view list below in this table for specified node group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&nodegroup=<Name>. Refer to the table under the Launching view section for attribute description. nodegroup nodegroup nodegroupid nodegroupuuid Refer to the table under the Launching view section for attribute description. Use the following URL to show a view, specified in the view list below in this table for specified interface group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&interfacegroup=<Name> interfacegroup ifgroup ifgroupid ifgroupuuid Refer to the table under the Launching view section for attribute description.   Use the following URL to show a view, specified in the view list above in a table with custom window settings: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&menus= <true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value>. Launching Management Mode Workspace views The following table presents the required attributes and their values for launching Management Mode workspace-specific views: Attributes Values Description Use the following URL to show a view, specified in the view list below in this table: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x> view allNodesTableView allInterfacesTableView allIPAddressTableView allIPSubnetsTableView allVlansTableView allLayer2ConnectionsTableView nodesByDeviceCategoryTableView interfacesByIfTypeTableView customNodeTableView customInterfaceTableView customIPAddressTableView routerRedundancyGroupsTableView nodeGroupsTableView interfaceGroupsTableView allManagementStationsTableView Nodes Interfaces IP Addresses IP Subnets VLANs Layer 2 Connections Nodes by Device Category Interfaces by IfType Custom Nodes Custom Interfaces Custom IP Addresses Router Redundancy Groups Node Groups Interface Groups Management Stations Use the following URL to show a view, specified in the view list below in this table for specified node group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&nodegroup=<Name>. Refer to the table under the Launching view section for attribute description. nodegroup nodegroup nodegroupid nodegroupuuid Refer to the table under the Launching view section for attribute description. Use the following URL to show a view, specified in the view list below in this table for specified interface group: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view= <x>&interfacegroup=<Name> interfacegroup ifgroup ifgroupid ifgroupuuid Refer to the table under the Launching view section for attribute description. Use the following URL to show a view, from the Management Mode workspace, with specified window settings: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value>. Launching a Configuration Workspace view The following table presents the required attributes and their values for launching the Configuration workspace-specific views: Attributes Values Description Use the following URL to show a view from Configuration workspace for a view specified below: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>. view nodeGroupsTableView allNodeGroupMapSettingsTableView interfaceGroupsTableView allManagementStationsTableView ramsServerTableView allURLActionInfosTableView allAccountsTableView allIfTypesTableView allDeviceProfilesTableView Node Groups Node Group Map Settings Interface Groups Management Stations RAMS Servers URL Actions User Accounts and Roles IfTypes Device Profiles   Use the following URL to show a view, from Configuration workspace, with specified window parameters: http://<serverName>:<portNumber>/nnm/launch?cmd=showView&view=<x>&menus=<true/false>&newWindow=<true/false>&envattrs=<name1=value>;<name2=value>.  
Read more
  • 0
  • 0
  • 14016

article-image-wxpython-28-window-layout-and-design
Packt
24 Dec 2010
8 min read
Save for later

wxPython 2.8: Window Layout and Design

Packt
24 Dec 2010
8 min read
wxPython 2.8 Application Development Cookbook Over 80 practical recipes for developing feature-rich applications using wxPython Develop flexible applications in wxPython. Create interface translatable applications that will run on Windows, Macintosh OSX, Linux, and other UNIX like environments. Learn basic and advanced user interface controls. Packed with practical, hands-on cookbook recipes and plenty of example code, illustrating the techniques to develop feature-rich applications using wxPython.        Once you have an idea of how the interface of your applications should look, it comes the time to put it all together. Being able to take your vision and translate it into code can be a tricky and often tedious task. A window's layout is defined on a two dimensional plane with the origin being the window's top-left corner. All positioning and sizing of any widgets, no matter what it's onscreen appearance, is based on rectangles. Clearly understanding these two basic concepts goes a long way towards being able to understand and efficiently work with the toolkit. Traditionally in older applications, window layout was commonly done by setting explicit static sizes and positions for all the controls contained within a window. This approach, however, can be rather limiting as the windows will not be resizable, they may not fit on the screen under different resolutions, trying to support localization becomes more difficult because labels and other text will differ in length in different languages, the native widgets will often be different sizes on different platforms making it difficult to write platform independent code, and the list goes on. So, you may ask what the solution to this is. In wxPython, the method of choice is to use the Sizer classes to define and manage the layout of controls. Sizers are classes that manage the size and positioning of controls through an algorithm that queries all of the controls that have been added to the Sizer for their recommended best minimal sizes and their ability to stretch or not, if the amount of available space increases, such as if a user makes a dialog bigger. Sizers also handle cross-platform widget differences, for example, buttons on GTK tend to have an icon and be generally larger than the buttons on Windows or OS X. Using a Sizer to manage the button's layout will allow the rest of the dialog to be proportionally sized correctly to handle this without the need for any platform-specific code. So let us begin our adventure into the world of window layout and design by taking a look at a number of the tools that wxPython provides in order to facilitate this task. Using a BoxSizer A BoxSizer is the most basic of Sizer classes. It supports a layout that goes in a single direction—either a vertical column or a horizontal row. Even though it is the most basic to work with, a BoxSizer is one of the most useful Sizer classes and tends to produce more consistent cross-platform behavior when compared to some of the other Sizers types. This recipe creates a simple window where we want to have two text controls stacked in a vertical column, each with a label to the left of it. This will be used to illustrate the most simplistic usage of a BoxSizer in order to manage the layout of a window's controls. How to do it... Here we define our top level Frame, which will use a BoxSizer to manage the size of its Panel: class BoxSizerFrame(wx.Frame): def __init__(self, parent, *args, **kwargs): super(BoxSizerFrame, self).__init__(*args, **kwargs)         # Attributes self.panel = BoxSizerPanel(self)         # Layout sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.panel, 1, wx.EXPAND) self.SetSizer(sizer) self.SetInitialSize() The BoxSizerPanel class is the next layer in the window hierarchy, and is where we will perform the main layout of the controls: class BoxSizerPanel(wx.Panel): def __init__(self, parent, *args, **kwargs): super(BoxSizerPanel, self).__init__(*args, **kwargs)         # Attributes self._field1 = wx.TextCtrl(self) self._field2 = wx.TextCtrl(self)         # Layout self._DoLayout() Just to help reduce clutter in the __init__ method, we will do all the layout in a separate _DoLayout method: def _DoLayout(self): """Layout the controls""" vsizer = wx.BoxSizer(wx.VERTICAL) field1_sz = wx.BoxSizer(wx.HORIZONTAL) field2_sz = wx.BoxSizer(wx.HORIZONTAL)     # Make the labels field1_lbl = wx.StaticText(self, label="Field 1:") field2_lbl = wx.StaticText(self, label="Field 2:")     # Make the first row by adding the label and field # to the first horizontal sizer field1_sz.AddSpacer(50) field1_sz.Add(field1_lbl) field1_sz.AddSpacer(5) # put 5px of space between field1_sz.Add(self._field1) field1_sz.AddSpacer(50) # Do the same for the second row field2_sz.AddSpacer(50) field2_sz.Add(field2_lbl) field2_sz.AddSpacer(5) field2_sz.Add(self._field2) field2_sz.AddSpacer(50)     # Now finish the layout by adding the two sizers # to the main vertical sizer. vsizer.AddSpacer(50) vsizer.Add(field1_sz) vsizer.AddSpacer(15) vsizer.Add(field2_sz) vsizer.AddSpacer(50)     # Finally assign the main outer sizer to the panel self.SetSizer(vsizer) How it works... The previous code shows the basic pattern of how to create a simple window layout programmatically, using sizers to manage the controls. First let's start by taking a look at the BoxSizerPanel class's _DoLayout method, as this is where the majority of the layout in this example takes place. First, we started off by creating three BoxSizer classes: one with a vertical orientation, and two with a horizontal orientation. The layout we desired for this window requires us to use three BoxSizer classes and this is why. If you break down what we want to do into simple rectangles, you will see that: We wanted two TextCtrl objects each with a label to the left of them which can simply be thought of as two horizontal rectangles. We wanted the TextCtrl objects stacked vertically in the window which is just a vertical rectangle that will contain the other two rectangles. This is illustrated by the following screenshot (borders are drawn in and labels are added to show the area managed by each of Panel's three BoxSizers): In the section where we populate the first horizontal sizer (field1_sz), we use two of the BoxSizer methods to add items to the layout. The first is AddSpacer, which does simply as its named and adds a fixed amount of empty space in the left-hand side of the sizer. Then we use the Add method to add our StaticText control to the right of the spacer, and continue from here to add other items to complete this row. As you can see, these methods add items to the layout from left to right in the sizer. After this, we again do the same thing with the other label and TextCtrl in the second horizontal sizer. The last part of the Panel's layout is done by adding the two horizontal sizers to the vertical sizer. This time, since the sizer was created with a VERTICAL orientation, the items are added from top to bottom. Finally, we use the Panel's SetSizer method to assign the main outer BoxSizer as the Panel's sizer. The BoxSizerFrame also uses a BoxSizer to manage the layout of its Panel. The only difference here is that we used the Add method's proportion and flags parameters to tell it to make the Panel expand to use the entire space available. After setting the Frame's sizer, we used its SetInitialSize method, which queries the window's sizer and its descendents to get and set the best minimal size to set the window to. We will go into more detail about these other parameters and their effects in the next recipe. There's more... Included below is a little more additional information about adding spacers and items to a sizer's layout. Spacers The AddSpacer will add a square-shaped spacer that is X pixels wide by X pixels tall to the BoxSizer, where X is the value passed to the AddSpacer method. Spacers of other dimensions can be added by passing a tuple as the first argument to the BoxSizer's Add method. someBoxSizer.Add((20,5)) This will add a 20x5 pixel spacer to the sizer. This can be useful when you don't want the vertical space to be increased by as much as the horizontal space, or vice versa. AddMany The AddMany method can be used to add an arbitrary number of items to the sizer in one call. AddMany takes a list of tuples that contain values that are in the same order as the Add method expects. someBoxSizer.AddMany([(staticText,), ((10, 10),), (txtCtrl, 0, wx.EXPAND)])) This will add three items to the sizer: the first two items only specify the one required parameter, and the third specifies the proportion and flags parameters.
Read more
  • 0
  • 0
  • 2659

article-image-using-groovy-closures-instead-template-method
Packt
23 Dec 2010
3 min read
Save for later

Using Groovy Closures Instead of Template Method

Packt
23 Dec 2010
3 min read
  Groovy for Domain-Specific Languages Extend and enhance your Java applications with Domain Specific Languages in Groovy Build your own Domain Specific Languages on top of Groovy Integrate your existing Java applications using Groovy-based Domain Specific Languages (DSLs) Develop a Groovy scripting interface to Twitter A step-by-step guide to building Groovy-based Domain Specific Languages that run seamlessly in the Java environment         Read more about this book       (For more resources on Groovy, see here.) Template Method Pattern Overview The template method pattern often applies during the thought "Well I have a piece of code that I want to use again, but I can't use it 100%. I want to change a few lines to make it useful." In general, using this pattern involves creating an abstract class and varying its implementation through abstract hook methods. Subclasses implement these abstract hook methods to solve their specific problem. This approach is very effective and is used extensively in frameworks. However, closures provide an elegant solution. Sample HttpBuilder Request It is best to illustrate the closure approach with an example. Recently I was developing a consumer of REST webservices with HttpBuilder. With HttpBuilder, the client simply creates the class and issues an HTTP call. The framework waits for a response and provides hooks for processing. Many of the requests being made were very similar to one another, only the URI was different. In addition, each request needed to process the returned XML differently, as the XML received would vary. I wanted to use the same request code, but vary the XML processing. To summarize the problem: HttpBuilder code should be reused Different URIs should be sent out with the same HttpBuilder code Different XML should be processed with the same HttpBuilder code Here is my first draft of HttpBuilder code. Note the call to convertXmlToCompanyDomainObject(xml). static String URI_PREFIX = '/someApp/restApi/' private List issueHttpBuilderRequest(RequestObject requestObj, String uriPath) { def http = new HTTPBuilder("http://localhost:8080/") def parsedObjectsFromXml = [] http.request(Method.POST, ContentType.XML) { req -> // set uri path on the delegate uri.path = URI_PREFIX + uriPath uri.query = [ company: requestObj.company, date: requestObj.date type: requestObj.type ] headers.'User-Agent' = 'Mozilla/5.0' // when response is a success, parse the gpath xml response.success = { resp, xml -> assert resp.statusLine.statusCode == 200 // store the list parsedObjectsFromXml = convertXmlToCompanyDomainObject(xml) } // called only for a 404 (not found) status code: response.'404' = { resp -> log.info 'HTTP status code: 404 Not found' } } parsedObjectsFromXml } private List convertXmlToCompanyDomainObject(GPathResult xml) { def list = [] // .. implementation to parse the xml and turn into objects } As you can see, URI is passed as a parameter to issueHttpBuilderRequest. This solves the problem of sending different URIs, but what about parsing the different XML formats that are returned? Using Template Method Pattern The following diagram illustrates applying the template method pattern to this problem. In summary, we need to move the issueHttpBuilderRequest code to an abstract class, and provide an abstract method convertXmlToDomainObjects(). Subclasses would provide the appropriate XML conversion implementation.
Read more
  • 0
  • 0
  • 12060
article-image-moodle-19-testing-and-assessment-advanced-options-quiz
Packt
23 Dec 2010
7 min read
Save for later

Moodle 1.9 Testing and Assessment: Advanced Options in Quiz

Packt
23 Dec 2010
7 min read
  Moodle 1.9 Testing and Assessment Develop and evaluate quizzes and tests using Moodle modules Create and evaluate interesting and interactive tests using a variety of Moodle modules Create simple vocabulary or flash card tests and complex tests by setting up a Lesson module Motivate your students to excel through feedback and by keeping their grades online A well-structured practical guide packed with illustrative examples and screenshots           Read more about this book       (For more resources on Moodle 1.9, see here.) Adding images to multiple-choice questions You are not going to always want simple text-based questions or answers. One common type of question that instructors frequently use is the image-based question. This item incorporates an image into the question. These questions are easy to create and can offer new dimensions for questions, as well as make the questions and test much more interesting to students. You can add images to any question or any field that allows you to use the rich-text editor, but we are going to use a single-answer, multiple choice question. We will follow the same basic steps as before. Step 1 We need to create a new multiple-choice question. When we are editing the question, we need to add the question name. We then need to add the question text. The question text we will be using for ours will be Which holiday is this girl celebrating? Step 2 We now go to the toolbar and click on the Insert Image icon. It is the icon that looks like a framed picture of a mountain, located two places to the left of the smiley face icon. Once we click on this icon, a pop-up menu will appear, as shown in the next screenshot: Here we have a few options in regards to how to use images and how they will be displayed in the question. Image URL & Alternate text If we use this option, we are able to take images directly from the Internet and use them for our tests. To use it we first need to have the address where the image is found. We are not looking for the address of the site here, but just the image. If you simply link to the web page, it will not work. To get just the image address, click on the image and you should get a menu with one of the options being View Image. Select View Image and you will be taken to a different page with only that image. This is the address you want to use. Once you have the image address, you copy and paste it to the Image URL text area.With the image address entered, we need to give the image a title in the Alternate text area. You can use anything you'd like here, but I tend to use the image name itself if it describes the image. If not, I create a short descriptive text of the image, something like "Girl celebrating Halloween". After you have entered text in both the Image URL and the Alternate text, click on the OK button and the image will be added to your question. It is important to note that if the website you pulled the image from removes it or changes its location, it will not be available for the question. It is therefore advisable to download the image, so that it will always be available to you. When you have finished adding responses and saving the question, you will see something like the following screenshot: Source: Image: Tina Phillips / FreeDigitalPhotos.net Now, looking back at the options available in the Insert image pop-up, you see three formatting options directly under the Image URL and Alternate text box where we were just working. They are called: Layout, Spacing, and Size. Layout In this fieldset, we are given ways to alter the Alignment and the Border thickness. Note that the image may be displayed differently on different browsers, although the CSS of the theme you are using will usually provide the appropriate margins and padding Alignment There are several options available here that show how the text and the image will be displayed. The full list is shown in the next screenshot: Most of these options are self-explanatory: Left will place the image to the left of the text, Right will place the image to the right of the text, and so on. However, there are a few possibly new terms. Texttop, Baseline, and Absbottom are HTML terms that many people might be unsure of. Texttop simply means that the very top of the tallest text (for example, l, b) will be aligned with the top of the image. This function works same as Top with some browsers. Baseline means that the imaginary line that all letters sit on will be aligned with the bottom of the image. In most browsers today, this functions the same as Bottom. Absbottom means that the letters that go below the baseline are aligned with the bottom of the image (for example, g, j). The top option, Not Set will place the image wherever the cursor is, without any special guide as to how it should be displayed. Border thickness The image you put into the question should look identical to the image you chose to use. If you are placing this image inside of text, or the edges are indistinct, or you simply want to frame it, use Border Thickness By placing a number in the Border thickness box, we will create a black border around the image. A small number will give a narrow border and a bigger number will give a thicker one. Here are three images showing the difference in borders. The first is set with a border of 0, the second has a border of 5, and the third with 10. You will notice that the image size itself is the same, but the border causes the viewable area of the image to compress Source: Images courtesy of: freeimages.co.uk Spacing There are two spacing options available, Horizontal and Vertical. The larger the number entered, the more space there is between the text and the images. Horizontal This setting allows you to set the horizontal distance between the image and the text surrounding it. This option can be useful if you need to have the image set apart from the text in the question or explanation. Vertical This setting is like the horizontal setting. It allows you to set the vertical distance between text and the image. This option can be useful if you need to have set distances between the text or have multiple images in a list with text surrounding them. Size The two options here are Width and Height. These two settings allow you to alter the size of the image; smaller numbers will make the image smaller and probably easier to work with. Note that the actual images are not resized. For the best result, first resize the images on your computer to the size you want them to be. Width This setting allows you to alter the width of the image. Altering this setting without altering the height will produce narrow or wide images, depending on whether you adjust the value up or down. Height This setting allows you to alter the height of the image. This option functions just like Width, and will allow you to produce images that are vertically stretched or shrunk. File Browser In this space, you will see any images that have been uploaded to the course. As you can see in the previous screenshot, it is empty, which tells us that there aren't pictures available in the course yet. If you look below File Browser, you will see four options for images uploaded to the course. You can Delete, Move, Zip, or Rename any images that have already been uploaded into the course. Preview This is where you can view any images that have been added to the course. This feature can be useful if you have a lot of images and tend to forget which images are which.
Read more
  • 0
  • 0
  • 2866

article-image-tips-tricks-mysql-python
Packt
23 Dec 2010
5 min read
Save for later

Tips & Tricks on MySQL for Python

Packt
23 Dec 2010
5 min read
  MySQL for Python Integrate the flexibility of Python and the power of MySQL to boost the productivity of your Python applications Implement the outstanding features of Python's MySQL library to their full potential See how to make MySQL take the processing burden from your programs Learn how to employ Python with MySQL to power your websites and desktop applications Apply your knowledge of MySQL and Python to real-world problems instead of hypothetical scenarios A manual packed with step-by-step exercises to integrate your Python applications with the MySQL database server         Read more about this book       (For more resources on this subject, see here.) Objective: Install a C compiler on Windows installation. Tip: Windows binaries do not currently exist for the 1.2.3 version of MySQL for Python. To get them, you would need to install a C compiler on your Windows installation and compile the binary from source. Objective: Use tar.gz to use egg file. Tip: If you cannot use egg files or if you use an earlier version of Python, you should use the tar.gz file, a tar and gzip archive. The tar.gz archive follows the Linux egg files in the file listing. The current version of MySQL for Python is 1.2.3c1, so the file we want is as following: MySQL-python-1.2.3c1.tar.gz This method is by far more complicated than the others. If at all possible, use your operating system's installation method or an egg file. Objective: Limitation of using MySQL for Python on Python version. Tip: This version of MySQL for Python is compatible up to Python 2.6. It is worth noting that MySQL for Python has not yet been released for Python 3.0 or later versions. In your deployment of the library, therefore, ensure that you are running Python 2.6 or earlier. As noted, Python 2.5 and 2.6 have version-specifi c releases. Prior to Python 2.4, you will need to use either a tar.gz version of the latest release or use an older version of MySQL for Python. The latter option is not recommended. Objective: It is important to phrase the query in such a way as to narrow the returned values as much as possible. Tip: Here, instead of returning whole records, we tell MySQL to return only the namecolumn. This natural reduction in the data reduces processing time for both MySQL and Python. This saving is then passed on to your server in the form of more sessions able to be run at one time. Objective: This hard-wiring of the search query allows us to test the connection before coding the rest of the function. Tip: There may be a tendency here to insert user-determined variables immediately. With experience, it is possible to do this. However, if there are any doubts about the availability of the database, your best fallback position is to keep it simple and hardwired. This reduces the number of variables in making a connection and helps one to blackbox the situation, making troubleshooting much easier. Objective: Readability counts. Tip: The virtue of readability in programming is often couched in terms of being kind to the next developer who works on your code. There is more at stake, however. With readability comes not only maintainability but control. If it takes you too much effort to understand the code you have written, you will have a harder time controlling the program's flow and this will result in unintended behavior. The natural consequence of unintended program behavior is the compromising of process stability and system security. Objective: Quote marks not necessary when assigning MySQL statements. Tip: It is not necessary to use triple quote marks when assigning the MySQL sentence to statement or when passing it to execute(). However, if you used only a single pair of either double or single quotes, it would be necessary to escape every similar quote mark. As a stylistic rule, it is typically best to switch to verbatim mode with the triple quote marks in order to ensure the readability of your code. Objective: xrange() is much more memory efficient than range(). Tip: The differences between xrange() and range() are often overlooked or even ignored. Both count through the same values, but they do it differently. Where range() calculates a list the first time it is called and then stores it in memory, xrange() creates an immutable sequence that returns the next in the series each time it is called. As a consequence, xrange() is much more memory efficient than range(), especially when dealing with large groups of integers. As a consequence of its memory efficiency, however, it does not support functionality such as slicing, which range() does, because the series is not yet fully determined. Objective: autocommit feature is useful in MySQL for Python . Tip: Unless you are running several database threads at a time or have to deal with similar complexity, MySQL for Python does not require you to use either commit() or close(). Generally speaking, MySQL for Python installs with an autocommit feature switched on. It thus takes care of committing the changes for you when the cursor object is destroyed. Similarly, when the program terminates, Python tends to close the cursor and database connection as it destroys both objects.
Read more
  • 0
  • 0
  • 5412
Modal Close icon
Modal Close icon