Object attribute lookup in Python

Posted September 4th, 2015 in Uncategorized by Florentin

Resources

descriptors

  • https://docs.python.org/2/howto/descriptor.html
  • http://stackoverflow.com/questions/3798835/understanding-get-and-set-and-python-descriptors

python 2 docs

  • https://docs.python.org/2/reference/datamodel.html?highlight=metaclass

great insights

  • http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/

__mro__ (Method Resolution Order)

  • http://stackoverflow.com/questions/2010692/what-does-mro-do-in-python
  • http://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-python-classes

simulate a __getattribute__ call

  • http://codereview.stackexchange.com/questions/92497/simulate-object-getattribute-in-python

Definitions

  • Everything in Python is an object (i.e. classes, modules, the numbers, the strings, etc)
  • A class is also an object
  • Every object is an instance of a class (example: isinstance(5, int) )
  • Because of that, every class is an instance of a special kind of class called metaclass
  • An instance is created by calling a class object
  • A non-data descriptor is an object following the data descriptor protocol as described in the docs
  • A data descriptor is a descriptor which defined both the __set__ AND __get__ methods
  • __mro__ is a tuple of classes that are considered when looking for base classes during method resolution

Code snippet

demo code

Instance attribute look up

Manual excerpt

The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to getattr() if provided.

Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this:

  • Get the Class from Instance
  • Call the Class’s special method __getattribute__. All objects have a default __getattribute__

Inside __getattribute__

  • Get the Class’s __mro__ as ClassParents
  • For each ClassParent in ClassParents
    • If the Attribute is in the ClassParent’s __dict__
    • If is a data descriptor
      • Return the result from calling the data descriptor’s special method __get__()
    • Break the for each (do not continue searching the same Attribute any further)
  • If the Attribute is in Instance’s __dict__
    • Return the value as it is (even if the value is a data descriptor)
  • For each ClassParent in ClassParents
    • If the Attribute is in the ClassParent’s __dict__
    • If is a non-data descriptor
      • Return the result from calling the non-data descriptor’s special method __get__()
    • If it is NOT a descriptor
      • Return the value
  • If Class has the special method __getattr__
    • Return the result from calling the Class’s special method __getattr__.
  • Raises an AttributeError

Things to remember (from the manual)

  • descriptors are invoked by the getattribute() method
  • overriding getattribute() prevents automatic descriptor calls
  • getattribute() is only available with new style classes and objects
  • object.getattribute() and type.getattribute() make different calls to get().
  • data descriptors always override instance dictionaries.
  • non-data descriptors may be overridden by instance dictionaries.

Class attribute look up

Given a MetaClass “M” and a Class “C” instance of the Metaclass “M”, calling “C.name” means looking up an Attribute “name” on the Class “C” like this:

  • Get the Metaclass from Class
  • Call the Metaclass’s special method __getattribute__

Inside __getattribute__

  • Get the Metaclass’s __mro__ as MetaParents
  • For each MetaParent in MetaParents
    • If the Attribute is in the MetaParent’s __dict__
    • If is a data descriptor
      • Return the result from calling the data descriptor’s special method __get__()
    • Break the for each
  • Get the Class’s __mro__ as ClassParents
  • For each ClassParent in ClassParents
    • If the Attribute is in the ClassParent’s __dict__
    • If is a (data or non-data) descriptor
      • Return the result from calling the descriptor’s special method __get__()
    • Else
      • Return the value
  • For each MetaParent in MetaParents
    • If the Attribute is in the MetaParent’s __dict__
    • If is a non-data descriptor
      • Return the result from calling the non-data descriptor’s special method __get__()
    • If it is NOT a descriptor
      • Return the value
  • If MetaClass has the special method __getattr__
    • Return the result from calling the MetaClass’s special method __getattr__.
  • Raises an AttributeError

A guide to using Google’s API Client Library for JavaScript with Sheets API

Posted August 12th, 2015 in Javascript, Uncategorized by Florentin

Google allow users to interact with Google Apps (like Calendar, Contacts, Drive) though APIs.
Working with the API Client Library for JavaScript in a browser environment presents it’s challenges as described below.

Setup

I’m making a single page application (one html file) to test the Javascript library capabilities for using it with Google Sheets Api.
Wish to demo the following:
* create a new spreadsheet
* search for the newly created file by name
* read rows and columns
* insert new rows

Working with private data requires setting up a project at https://console.developers.google.com/project
Read the chapter “Get access keys for your application” from https://developers.google.com/api-client-library/javascript/start/start-js
Make sure to add “Drive API” here https://console.developers.google.com/project/PROJECT_KEY/apiui/apis/enabled
After “Create new client id” steps, write down the “Client ID” and make sure that JavaScript origins matches the demo server (in my case it was http://localhost:8888)

I’ll start building the demo with the help of these resources:

https://developers.google.com/api-client-library/javascript/samples/samples

https://github.com/google/google-api-javascript-client/blob/master/samples/authSample.html

The demo

This depends on “client.js” which is a single line js file like this: window.clientId = ‘aaa-bbbcccddd.apps.googleusercontent.com';

Here’s the file on GITHUB: https://github.com/florentin/google-sheets-api

Please read the code comments, they are important.

<!DOCTYPE html>
<html>
	<head>
	  <meta charset='utf-8' />
	</head>
	<body>
		<h1>Hello Google Sheets API</h1>
		<em>Open the console and watch for errors and debug messages.</em>
		<div id="step1">
		    <h2>Step 1: Authorize this app with your Google account</h2>
		    <span id="authorize-status"></span>
		    <button id="authorize-button" style="visibility: hidden">Authorize</button>
		</div>
		
		<div id="step2">
            <h2>Step 2: Create a Spreadsheet document in Google Drive</h2>
            <span id="create-status"></span>
            <button id="create-button">Create file in Drive</button>
            <em>Add a file named "blocky" to Google Drive</em>
        </div>
        
        <div id="step3">
            <h2>Step 3: Search spreadsheet files by the name "blocky"</h2>
            <span id="list-status"></span>
            <button id="list-button">Search files (json)</button>
            
        </div>
        
        <div id="step4">
            <h2>Step 4: Find the first file named "blocky" and retrieve the worksheets</h2>
            <span id="worksheets-status"></span>
            <button id="worksheets-button">Retrieve worksheets</button>
        </div>
        
        <div id="step5">
            <h2>Step 5: Add rows to the first worksheet from the spreadsheet "blocky"</h2>
            <span id="rows-status"></span>
            <button id="rows-button">Add rows</button>
        </div>
		
		<script src="client.js"></script>
        
        <script type="text/javascript">
		var scopes = 'https://spreadsheets.google.com/feeds',
		    headers = {
				accept_json: {'Accept': 'application/json'},
				accept_xml: {'Accept': 'application/atom+xml'},
				send_json: {'Content-Type': 'application/json'},
				send_xml: {'Content-Type': 'application/atom+xml'}
		    },    
		    authorizeButton = document.getElementById('authorize-button'),
		    authorizeStatus = document.getElementById('authorize-status'),
	        createButton = document.getElementById('create-button'),
	        listButton = document.getElementById('list-button'),
	        worksheetsButton = document.getElementById('worksheets-button'),
	        rowsButton = document.getElementById('rows-button');

		function handleClientLoad() {
			checkAuth();
			createButton.onclick = handleCreateClick;
			listButton.onclick = handleListClick;
			worksheetsButton.onclick = handleWorksheetsClick;
			rowsButton.onclick = handleRowClick;
		}
    
		function checkAuth() {
		    gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
		}
    
		function handleAuthResult(authResult) {
			if (authResult && !authResult.error) {
				// this app is authorised
				authorizeButton.style.visibility = 'hidden';
				authorizeStatus.innerHTML = 'Is Authorised';
				//makeApiCall();
			} else {
				authorizeButton.style.visibility = '';
				authorizeButton.onclick = handleAuthClick;
				authorizeStatus.innerHTML = 'Not Authorised';
			}
		}
    
		function handleAuthClick(event) {
			gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
		    return false;
		}
    
		function request(method, url, post_data, sucess_call, headers) {
			var oauthToken = gapi.auth.getToken(),
			    xhr = new XMLHttpRequest();
			
			console.info('request url', url);
			
			xhr.open(method, url);
			xhr.setRequestHeader('Authorization', 'Bearer ' + oauthToken.access_token);
			//xhr.setRequestHeader("GData-Version", "3.0");
			if(headers !== undefined) {
				for(var key in headers) {
					xhr.setRequestHeader(key, headers[key]);
				}
			}
			
			xhr.onload = function() {
			    console.info('response xhr', xhr);
			    if(typeof sucess_call === "function")
			        sucess_call(xhr);
			};
			
			xhr.onerror = function(err) {
			    console.error('Get Woops', err);
			};
			
			xhr.send(post_data);
		}
	    
		function get_request(url, success_call, headers) {
			return request('GET', url, null, success_call, headers);
		}
		
		function post_request(url, data, success_call, headers) {
            return request('POST', url, data, success_call, headers);
        }
		
		function json_url(url) {
		    return url+'?alt=json';
		}
		
		function serialize(obj) {
            return '?'+Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')
        }
        
        function xhr_json_response(xhr) {
            return JSON.parse(xhr.responseText);
        }
		
		function handleCreateClick(event) {
			/*
			To upload a file, check the Javascript example
			from https://developers.google.com/drive/v2/reference/files/insert
			*/
			var body = {
				    'mimeType': 'application/vnd.google-apps.spreadsheet',
	                'title': 'blocky'},
	            url = 'https://www.googleapis.com/drive/v2/files';
			post_request(url, JSON.stringify(body), null, send_json_header);
		}
		
		function handleListClick(event, callback) {
			/*
			find the Drive Api reference here

https://developers.google.com/drive/v2/reference/

			*/
			var url = 'https://www.googleapis.com/drive/v2/files'+serialize({'q': 'title="blocky" and trashed=false'});   
			get_request(url, function(xhr) {
				var obj = xhr_json_response(xhr);
				console.info('documents', obj, obj.items);
				if(typeof callback === "function") {
					callback(obj);
				}
			}); 
		}
		
		function handleWorksheetsClick(events) {
			/*
			Google Sheets Api has no "reference" page and i 
			couldn't figure out a way to find a spreadsheet based on it's id.

https://developers.google.com/google-apps/spreadsheets/index

			Make sure that the "blocky" spreadsheet has the following rows:
	        key   content url
	        a1    a2      a3
	        b1    b2      b3
			*/
			
			handleListClick(null, function(obj) {
				// This is the endpoint for getting the worksheets of a spreadsheet identified by "id"
                var url = 'https://spreadsheets.google.com/feeds/worksheets/'+encodeURIComponent(obj.items[0].id)+'/private/full';
				
                /*
				=== TROUBLE AHEAD ===
                Google Sheets Api by default returns and accepts "application/atom+xml" content type - that's XML strings
                */
                
                // Let's get an "ATOM+XML" response of the worksheets for our "blocky" spreadsheet
                get_request(url, function(xhr) {
                	console.info('Request atom+xml', xhr);
                }, headers.accept_xml);
                
                /*
                The request doesn't get thought because of this error:
                No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8888' is therefore not allowed access.
                
                If you check the Network Headers in Chrome Developer tools, 
                Chrome first does a "OPTIONS" request to the "url" and gets:
                    access-control-allow-headers:authorization
                    access-control-allow-methods:GET,OPTIONS
                    access-control-allow-origin:http://localhost:8888
                
                All seems fine until it makes the actual "GET" request where the "access-control-allow-origin" header dissapears 
                and Google Chrome denies the cross origin request. 
                */
                
                /*
                Let's alter the "url" to request a "JSON" feed as described here: 

https://developers.google.com/gdata/docs/json

                */
                
                get_request(url+'?alt=json', function(xhr) {
                    console.info('Request json', xhr);
                    var obj = xhr_json_response(xhr);
                    console.log('json', obj, obj.feed.entry[0]);
                    
                    /*
                    Using a JSON request (?alt=json) allows getting the data out of the worksheets.
                    Manually add some data to the "blocky" spreadsheet in Google Drive app to see results
                    */
                    var url = obj.feed.entry[0].link[0].href;
                    get_request(url+'?alt=json', function(xhr) {
                    	var obj = xhr_json_response(xhr);
                    	console.info('Spreadsheet data', obj, obj.feed.entry);
                    });
                }, headers.accept_json);
                
			});
		}
		
		function handleRowClick(events) {
			handleListClick(null, function(obj) {
				/*
		        Adding new rows as described in the chapter "Add a list row" from 

https://developers.google.com/google-apps/spreadsheets/data

		        is not working

		        */
				var url = 'https://spreadsheets.google.com/feeds/worksheets/'+encodeURIComponent(obj.items[0].id)+'/private/full';
				get_request(url+'?alt=json', function(xhr) {
                    console.info('Request json', xhr);
                    var obj = xhr_json_response(xhr);
                    //console.log('worksheet', obj)
                    
                    var url = obj.feed.entry[0].link[0].href;
                    var post_data = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended"><gsx:key>x1</gsx:key><gsx:content>x2</gsx:content><gsx:url>x3</gsx:url></entry>';
                    post_request(url, post_data, function(xhr) {
                    	console.log('post data', xhr);
                    	/*
                    	No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8888' is therefore not allowed access.
                    	*/
                    }, headers.send_xml);
				});
			});
		}
    </script>
    <script src="https://apis.google.com/js/auth.js?onload=handleClientLoad"></script>
  </body>
</html>

Conclusions

  • Google Sheets API is using the old “gdata API”. Read more https://developers.google.com/gdata/docs/directory and here https://developers.google.com/gdata/docs/json
  • Reading operations seem to work fine with CORS (https://developers.google.com/api-client-library/javascript/features/cors) requests as long as you request JSON feeds (add ?alt=json to the requested url)
  • I wasn’t able to “POST” data using CORS requests, using either “atom+xml” or “json” content types.
  • Sending “POST” requests from “curl” or Python worked fine.
  • All these basically mean that you can’t build Single-Page Apps for changing data in Google services (unless you use a back-end request proxy)

A better diigo bookmarklet (diigolet)

Posted March 29th, 2013 in Uncategorized by Florentin

What is the ‘diigolet’

The diigolet is a Diigo.com tool used to manage a user’s bookmarks. It is a bookmarklet, a bookmark stored in a browser that contains Javascript.
The original diigolet is here:

https://www.diigo.com/tools/diigolet

How is BetterDiigolet different ?

BetterDiigolet by default opens the “bookmark” menu from the diigolet toolbar, it fills in the description and clicks all recommended tags. All is left for the user is to click “Save Bookmark”.
If it’s used on a already bookmarked url, it will color the tags previously used in ‘green’. The new recommended tags will remain ‘blue’.
This is an easy way for the user to find out if the url has been bookmarked before.
By using better_diigolet.js one can save time with clicking though all the tags and filling in the bookmark’s description field.

How it’s built

Diigo’s diigolet is loading a js file named “diigolet_b_h_b.js” which in turns loads “diigolet.js”. diigolet.js contains jQuery v1.4.3.
BetterDiigolet loads everything in better_diigolet.js, jQuery has been replaced with v1.8.3
The downside is the missing “Loading Diigolet…” tooltip.

How to install it in Firefox

  1. Make sure the “Bookmarks Toolbar” is visible. If it is not, go to menu View > Toolbars.
  2. Drag this button: BetterDiigolet up to your Bookmarks Toolbar.

How Rational Are You?

Posted August 2nd, 2011 in Uncategorized by Florentin

Five questions to get you thinking

By Kurt Kleiner

I recommend reading the whole article here

Although intelligence as measured by IQ tests is important, so is the ability to think rationally about problems. The surprise is that less intelligent people usually perform just as well as highly intelligent people on problems that test rationality. Here are a few questions that test if you’re a rational thinker.

1. A bat and ball cost $1.10 in total. The bat costs $1 more than the ball. How much does the ball cost?

2. Is the following conclusion logically valid?

Premise 1: All living things need water.

Premise 2: Roses need water.

Therefore, roses are living things.

3. XYZ virus causes a disease in one in every 1,000 people. A test always correctly indicates if a person is infected. The test has a false-positive rate of five per cent – in other words, the test wrongly indicates that the XYZ virus is present in five per cent of the cases in which the person does not have the virus. What is the probability that an individual testing positive actually has the XYZ virus?

4. There are four cards on a table. Each has a letter on one side and a number on the other. The cards look like this:

K A 8 5

Here is a rule: If a card has a vowel on its letter side, it has an even number on its number side. Which card(s) must be turned over to find out if the rule is true or false?

5. According to a comprehensive study by the U.S. Department of Transportation, a particular German car is eight times more likely than a typical family car to kill the occupants of another car in a crash. The U.S. Department of Transportation is considering recommending a ban on the sale of this German car. Do you think the United States should ban the sale of this car?

Answers

1. Five cents. Many people, including students at MIT, Princeton and Harvard, automatically answer 10 cents. After all, a dollar plus 10 cents equals $1.10. But that cognitive shortcut doesn’t work, since it would mean the bat costs only 90 cents more than the ball.

2. No, it is not logical, even though 70 per cent of university students given the problem think it is. Although the conclusion is true, it doesn’t follow from the premises. Consider the same problem worded in a different way:

Premise 1: All insects need oxygen.

Premise 2: Mice need oxygen.

Therefore, mice are insects.

In the original problem, the tendency is to be a cognitive miser, and let the obvious truth of the conclusion substitute for reasoning about its logical validity. (In the second problem, though, our cognitive miser makes the problem easy.)

3. Two per cent. (Most people say 95 per cent.) If one in 1,000 people has the disease, 999 don’t. But with a five per cent false-positive rate, the test will show that almost 50 of them are infected. Of 51 patients testing positive, only one will actually be infected. The math here isn’t especially hard. But thinking the problem through is tricky.

4. A and 5. Ninety per cent of people get this one wrong, usually by picking A and 8. They think they need to confirm the rule by looking for a vowel on the other side of the 8. But the rule only says that vowels must have even numbers, not that consonants can’t. An odd number on the back of the A, or a vowel on the back of the 5, would show that the rule is false.

5. OK, there’s no right or wrong answer here. However, 78 per cent of the people Stanovich sampled thought the German car should be banned. But when he turned the question around so that Germany was considering banning an American car (he was quizzing people in the U.S., by the way), only 51 per cent thought Germany should ban the car. This is an example of “myside bias” – evaluating a problem from a standpoint that is biased toward your own situation.

El cartel rules

Posted March 15th, 2010 in Uncategorized by Florentin

1. You can’t succeed unless you try.

2. Always have an exit strategy.

3. When you’ve got the right strategy, execute.

4. There’s no such thing as a once in a lifetime opportunity.

5. When you discover you’ve executed the wrong strategy, stop.

6. Follow your passion.

7. Don’t ignore your problems.

8. The most valuable asset in any venture is thought.

9. Success begins by surrounding yourself with quality people.

10. Opportunity is a liability, not an asset.

11. Never ignore the signs.

12. Success comes by doing many things right, over a very long time.

13. Success is never guaranteed.

Synonyms find and replace tool

Posted March 15th, 2010 in Portfolio, Uncategorized by Florentin

Synonyms find and replace tool: based on FCKeditor, this tool was used to find and replace synonyms in a text. I have extracted all terms from Wordnet dictionary.