B
    EDbÂl  ã               @   s  d dl Z d dlZd dlZd dlZd dlZd dlZd dlT d dlmZ d dl	m
Z
 d dlmZ d dlmZ ejd  dkrŒd dlmZ d	d
„ Zn"ejd  dkr®d dlmZ dd
„ ZG dd„ deƒZG dd„ deƒZe d¡ae d¡ae d¡aG dd„ deƒZdadd„ ZdS )é    N)Ú*)Ú
interfaces)Ú	LadonType)Úvalidate_type)ÚTypeManageré   )Údetectc             C   s   | j S )N)Z	func_name)Úf© r
   ú:G:\easytimepro\master/mysite/ladon\ladonizer\collection.pyÚget_function_name   s    r   é   c             C   s   | j S )N)Ú__name__)r	   r
   r
   r   r      s    c               @   s‚   e Zd ZdZd dd„Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )!ÚLadonServiceCollectiona™  
	LadonServiceCollection collects all services, methods and types for a given server-run
	In Ladon it is only used as a singleton global object, but this could change in time.
	The singleton behaviour is implemented in global_service_collection() which creates the
	global object the first time it is called and then returns the same object the rest of
	the time.
	
	add_service_method() is the most important method on this class as it is the central
	entry-point used by the ladonize decorator to register a service method.
	
	Ladon uses Abstract Syntax Trees (ast) to parse the source files associated with the
	service methods that are registered. We do that because the methods are unbound at
	call-time of the decorators by the Python interpreter. To actually detect the name of
	the class that will have the method as member we do a manual parse per source file.
	
	Example::
		LadonServiceCollection
		 + LadonServiceInfo (UserService)
		    + LadonMethodInfo (addUser)
		    + LadonMethodInfo (delUser)
		 + LadonServiceInfo (RoleService)
		    + LadonMethodInfo (addRole)
		    + LadonMethodInfo (delRole)
		    + LadonMethodInfo (addRoleMember)
		      .
		      .
	Úglobalc             C   s(   i | _ i | _i | _i | _|| _d| _dS )z
		initialize some members
		é   N)Úsrc_infoÚsrc_encodingÚservicesÚservice_and_numberÚcollection_nameÚservice_counter)Úselfr   r
   r
   r   Ú__init__4   s    zLadonServiceCollection.__init__c             C   s,  || j kr| j | S i | j |< t|dƒ}| ¡ }| ¡  t|ƒ}t |¡}~|d | j|< xÆ|jD ]¼}t	|ƒtj
krb|}|j}|j}	g }
d}xz|jD ]p}|rêt	|ƒtjkrêt	|jƒtjkrêx.t |jj dd¡¡ d¡D ]}|
|g7 }
qØW t	|ƒtjkr |j}	d}q”W ||	|
f| j | |j< qbW | j | S )z}
		Parse a python source file at ast-level to internally acknowledge which
		class methods will be bound to which classes.
		ÚrbÚencodingTz
Ú
F)r   ÚopenÚreadÚcloser   ÚastÚparser   ZbodyÚtypeZClassDefÚlinenoZExprÚvalueZStrÚinspectZcleandocÚsÚreplaceÚsplitZFunctionDefÚname)r   ÚfnameZsrc_fpÚsrcr   ÚaÚobjÚcÚfirstlinenoZ
lastlinenoZclass_doc_linesZfirst_class_objZdoc_liner
   r
   r   Úsource_info?   s4    




""z"LadonServiceCollection.source_infoc          
   O   sÈ   |j }|jj}|jj}|  |¡}x¢| ¡ D ]–\}}	||	d kr*||	d kr*||f| jkr t|||||	d | j| j	| ƒ| j||f< ||f| j
| j< |  jd7  _| j||f j|f|ž|Ž}
|
S q*W dS )aZ  
		Register a class method. The method is expected to be registered a decorator
		and therefore unbound.
		If the class which the method is member of has not yet been registered a new
		LadonServiceInfo object will be created for it. The method itself will be
		associated with this object with the instantiation of a LadonMethodInfo
		object.
		r   r   r   N)Ú
__module__Ú__code__Úco_filenameÚco_firstlinenor0   Úitemsr   ÚLadonServiceInfor   r   r   Ú
add_method)r   r	   Údef_argsÚdef_kwÚ
modulenameZ	src_fnamer/   ÚsinfoZclsnameÚvÚmethodr
   r
   r   Úadd_service_methodj   s    

*z)LadonServiceCollection.add_service_methodc             C   s,   g }x"| j  ¡ D ]\}}||jg7 }qW |S )z0
		Return a list of registered service names.
		)r   r5   Úservicename)r   ÚnamesÚkr<   r
   r
   r   Úservicenames‹   s    z#LadonServiceCollection.servicenamesc             C   s    ||f| j kr| j ||f S dS )a  
		Return a specific LadonServiceInfo object registered with a certain
		source file and a service name. Service names are actually class names
		and you cannot have 2 active classes with the same name in one module
		therefore this method will not end in an ambigious result.
		N)r   )r   Ú
sourcefiler?   r
   r
   r   Úservice_by_source”   s    z(LadonServiceCollection.service_by_sourcec             C   s   || j kr| j| j |  S dS )z¤
		Return a specific LadonServiceInfo object registered with a certain
		service number. Service numbers are unique, so this cannot end in an
		ambigious result.
		N)r   r   )r   Úservicenumberr
   r
   r   Úservice_by_numberŸ   s    
z(LadonServiceCollection.service_by_numberc             C   sD   g }x:| j  ¡ D ],\}}|d  ¡ | ¡ kr|| j | g7 }qW |S )ac  
		Return LadonServiceInfo objects by service name. This will almost
		always result in a single object, but it is possible to register
		methods from classes with identical names but implementation in
		different modules. This is perfectly legal and therefore it is
		possible to have more than one LadonServiceInfo object returned
		with this method.
		r   )r   r5   Úlower)r   r?   ÚresrA   r<   r
   r
   r   Úservices_by_name©   s
    	z'LadonServiceCollection.services_by_namec             C   sR   |   |jj¡}|jj}x6| ¡ D ]*\}}||d kr ||d kr |  |¡S q W g S )a×  
		Return LadonServiceInfo objects by referenced service function (method).
		This will almost always result in a single object, but it is possible to register
		methods from classes with identical names but implementation in
		different modules. This is perfectly legal and therefore it is
		possible to have more than one LadonServiceInfo object returned
		with this method.
		
		@param fn: function (method) registered within the service
		@rtype: [LadonMethodInfo]
		r   r   )r0   r2   r3   r4   r5   rI   )r   Úfnr0   Zsource_lineÚ
class_namer<   r
   r
   r   Úservices_by_function¸   s    z+LadonServiceCollection.services_by_functionc             C   s>   d| j i}i |d< x&| j ¡ D ]\}}| ¡ |d |< qW |S )z$
		Create an serialization object
		Z
collectionr   )r   r   r5   Ú	serialize)r   rH   rA   r;   r
   r
   r   rM   Í   s
    
z LadonServiceCollection.serializec       	      C   s˜   |   ||¡r”||f}| j| }x| jD ]}| j| |kr&P q&W | j|= | j|= i }d}x,t| j ¡ ƒD ]}| j| ||< |d7 }qfW || _|| _|S dS )z«Removes a service from collection
		
		@param servicename: name of service to remove
		@param sourcefile: filename with service source code
		@rtype: LadonServiceInfo  
		r   F)Úhas_servicer   r   ÚsortedÚkeysr   )	r   rC   r?   rA   ÚserviceZnumZnew_service_and_numberZcounterZold_numr
   r
   r   Úremove_service×   s"    
z%LadonServiceCollection.remove_servicec             C   sX   g }t   | j¡}xB|D ]:}|d  ¡ | ¡ kr|  |d |d ¡}|r||g7 }qW |S )zðRemoves a service or list of services with the given name from the
		service collection and returns the removed elements
		
		@param servicename: name of a service to remove 
		@rtype: [LadonServiceInfo] - list of removed service objects
		r   r   )Úcopyr   rG   rR   )r   r?   rH   r   rA   rQ   r
   r
   r   Úservices_remove_by_nameö   s    
z.LadonServiceCollection.services_remove_by_namec             C   sH   |j |jf}|  |j |j¡sD|| j|< || j| j< |  jd7  _dS dS )z„Appends a new service to the end of service collection
		
		@param service: LadonServiceInfo - service info object
		@rtype: bool
		r   TF)rC   r?   rN   r   r   r   )r   rQ   rA   r
   r
   r   Úappend_service  s    
z%LadonServiceCollection.append_servicec             C   s   ||f| j kS )z±Checks if current collection contains the service
		
		@param sourcefile: path to the source file with service definition
		@param servicename: name of service
		@rtype: bool
		)r   )r   rC   r?   r
   r
   r   rN     s    z"LadonServiceCollection.has_servicec             C   s   t  | j| jdœ¡S )z)
		Make a pretty string representation
		)Zcollectionnamer   )ÚpprintÚpformatr   r   )r   r
   r
   r   Ú__str__!  s    zLadonServiceCollection.__str__N)r   )r   r1   Ú__qualname__Ú__doc__r   r0   r>   rB   rD   rF   rI   rL   rM   rR   rT   rU   rN   rX   r
   r
   r
   r   r      s   
+!	

	r   c               @   sp   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )r6   aA  
	LadonServiceInfo stores information about a service that has been registered
	using LadonServiceCollection.add_service_method().
	It also aggragates all it's methods as LadonMethodInfo objects. A per service
	type manager (ladon.types.typemanager.TypeManager) that keeps track of the
	types that the service requires.
	c             C   s<   || _ || _|| _|| _|| _|| _|| _i | _tƒ | _	d S )N)
r?   rE   r   rC   r:   Ú	doc_linesr#   Úmethodsr   Útypemanager)r   r?   rC   r:   r#   r[   r   r   r
   r
   r   r   /  s    zLadonServiceInfo.__init__c             O   s$   t | |f|ž|Ž}|| jt|ƒ< |S )a  
		Add a service method by creating a LadonMethodInfo object that analyses
		the function in regards to it's parameters and documentation.
		
		@param f The function object of the method to add
		@rtype LadonMethodInfo Return the method info of the method
		)ÚLadonMethodInfor\   r   )r   r	   r8   r9   r=   r
   r
   r   r7   =  s    zLadonServiceInfo.add_methodc             C   s   || j kr| j | S dS )zÔ
		Get the LadonMethodInfo object registered with *methodname*.
		
		@param methodname The name og the service method
		@rtype LadonMethodInfo The method info registered with methodname or None if non-existent
		N)r\   )r   Ú
methodnamer
   r
   r   r=   J  s    

zLadonServiceInfo.methodc             C   s<   t | j ¡ ƒ}| ¡  g }x|D ]}|| j| g7 }q W |S )zX
		Get a list of the service methods
		
		@rtype list List of LadonMethodInfo objects
		)Úlistr\   rP   Úsort)r   Zmethod_namesÚmethod_listZmnr
   r
   r   rb   U  s    
zLadonServiceInfo.method_listc             C   s@   | j | j| ji dœ}x&| j ¡ D ]\}}| ¡ |d |< q W |S )z]
		make a serialization object
		
		@rtype dict A serialization dictionary for the service
		)r?   rC   r   r\   r\   )r?   rC   r#   r\   r5   rM   )r   rH   rA   Zminfor
   r
   r   rM   b  s    zLadonServiceInfo.serializec             C   s   | j jS )zƒ
		Get a list of the primitives required by the service.
		
		@rtype list A distinct list of primitives required by this service
		)r]   Zprimitive_list)r   r
   r
   r   Úprimitive_requirementsq  s    z'LadonServiceInfo.primitive_requirementsc             C   s   | j jS )zð
		Query the service's typemanager to whether lists are required
		by the service. This information is used by the dispatcher to
		filter out service interfaces that don't support lists.
		
		@rtype bool Are lists required by the service
		)r]   Z	has_lists)r   r
   r
   r   Úlist_support_requiredy  s    z&LadonServiceInfo.list_support_requiredc             C   s   | j jS )zñ
		Query the service's typemanager to whether dicts are required
		by the service. This information is used by the dispatcher to
		filter out service interfaces that don't support dictss.
		
		@rtype bool Are dicts required by the service
		)r]   Z	has_dicts)r   r
   r
   r   Údict_support_requiredƒ  s    z&LadonServiceInfo.dict_support_requiredc             C   s$   |   | ¡ ¡s || j| ¡ < dS dS )zŽAppends a method defined as LadonMethodInfo object to the current service
		
		@param methodinfo: LadonMethodInfo object
		@rtype: boolean 
		TF)Ú
has_methodr)   r\   )r   Z
methodinfor
   r
   r   Úappend_method  s    zLadonServiceInfo.append_methodc             C   s$   |   |¡r | j| }| j|= |S dS )z~
		Removes the method with the given name from a service methods list
		
		@param methodname: name of the method to remove 
		F)rf   r\   )r   r_   r=   r
   r
   r   Úremove_method™  s
    

zLadonServiceInfo.remove_methodc             C   s
   || j kS )z|
		Checks if the method with given name is registered in the service
		
		@param methodname: name of the method to check 
		)r\   )r   r_   r
   r
   r   rf   ¦  s    zLadonServiceInfo.has_methodc             C   s   t  | j| j| j| jdœ¡S )zg
		Create a pretty string representation of the service info object
		
		@rtype String Pretty string
		)r?   r[   rC   r\   )rV   rW   r?   r[   rC   r\   )r   r
   r
   r   rX   ®  s    zLadonServiceInfo.__str__N)r   r1   rY   rZ   r   r7   r=   rb   rM   rc   rd   re   rg   rh   rf   rX   r
   r
   r
   r   r6   '  s   

r6   z"^@param\s+([_a-zA-Z0-9]+):\s*(.*)$z^@rtype:\s*(.*)$z^\s+(\S.*)$c               @   sn   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ Zddd„Z	ddd„Z
ddd„Zdd„ Zdd„ Zdd„ ZdS )r^   zõ
	Structure for method information. Objects of this class holds method,
	arg and return type documentation and type-info. User-options defined
	via keyword args to the method like *encoding* and *allow_unsafe_conversion*
	are also stored here.
	c          
   O   s  t  |¡}t  |¡}|r2tjd dkr2t||jƒ}|| _i | _|j	d k| _
|jdd … | _|j| _|j| _t|ƒ| _|| _|| _d| _d| _y|d | _W n. tk
rÈ } zt|| jƒ‚W d d }~X Y nX g | _d|krâ|d | _d|krô|d | _|j | j¡| _d| _| jd krtƒ | _| jd t | jƒ … | _!| jt | jƒ d … | _"t | jƒt |ƒkrvt#|| j| j| jƒ‚t | jƒ}t | jƒ}	xÔt$t | jƒƒD ]Â}
|  j|j ||
 ¡O  _d}|	||
  }|d	krd
}| j| }t%| j| ƒ}t&||
 |ƒst'|| j| j|
 | j|
 |ƒ‚| j|
 ||
 |g dœ| j| j|
 < |rš|| j| j|
  d< qšW g | _(|r | )dd¡}| *d¡}d }xv|D ]l}t+ ,|¡}|rö| -¡ d }|| jkrÄd }qŽ| -¡ d  .¡ }|rŽ| j| d  |g7  < qŽt/ ,|¡}|r4d}| -¡ d  .¡ }|rŽ|  j|g7  _qŽt0 ,|¡}|r’|r’|dkrj|  j| .¡ g7  _n$| j| d  | -¡ d  .¡ g7  < qŽt | .¡ ƒsæ|ræ|dkrÆ|  j| .¡ g7  _n| j| d  | .¡ g7  < qŽd }|  j(|g7  _(qŽW d 1| j(¡| _d S )Nr   r   r   zUTF-8FÚrtyper   Zallow_unsafe_conversionéÿÿÿÿT)r)   r"   ÚoptionalÚdocÚdefaultz
r   rl   Ú_rtype)2r%   Z
getargspecZgetdocÚsysÚversion_infoZunicoder   r;   Ú_argsÚkeywordsZ_has_keywordsÚargsÚ
_arg_namesÚdefaultsZ	_defaultsrZ   Z_docr   Ú
_func_nameÚ
_arg_typesÚ_real_methodZ	_encodingZ_allow_unsafe_conversionrn   ÚKeyErrorZReturnTypeUndefinedÚ
_rtype_docr]   Zanalyze_paramZ_multipart_response_requiredZ_multipart_request_requiredÚtupleÚlenZ_mandatory_argsZ_optional_argsZArgDefCountMismatchÚranger"   r   ZDefaultArgTypeMismatchZ_method_docr'   r(   Úrx_doc_paramsÚmatchÚgroupsÚstripÚrx_doc_rtypeÚrx_doc_param_linesÚjoin)r   r;   r	   r8   r9   Zargspecsrl   ZkeZalenZdlenÚargidxZhas_defaultZdefidxZdef_valZdef_typeZdoclinesZcur_argnameZdlineZparam_matchZdocline1Zrtype_matchZparam_line_matchr
   r
   r   r   Â  sº    













$
zLadonMethodInfo.__init__c             C   s   | j S )zx
		Fetch the name of the method being mapped by this LadonMethodInfo.
		
		@param rtype String The name of the method
		)rv   )r   r
   r
   r   r)   g  s    zLadonMethodInfo.namec             C   s(   g }x| j D ]}|| j| g7 }qW |S )zl
		Fetch information about this methods parameters.
		
		@rtype list A list of argument description dicts
		)rt   rq   )r   ZretÚargnamer
   r
   r   rs   o  s    zLadonMethodInfo.argsc             C   s   |   ¡ | j|  ¡ | j| jdœS )z
		Serialize this method
		)r_   rl   rs   ri   Z	rtype_doc)r)   rZ   rs   rn   rz   )r   r
   r
   r   rM   z  s    zLadonMethodInfo.serializec             C   s   | j S )zy
		Returns a reference to the real service method was decorated by ladonizer
		
		@rtype: function which was decorated
		)rx   )r   r
   r
   r   Úget_real_method€  s    zLadonMethodInfo.get_real_methodFNc       	      C   s¤   |   |¡s t|ƒtkr|g}||||dœ| j|< |dk	rF| j |¡}n$|dk	r`| j |¡d }n
t| jƒ}|g| j|d…< t| jƒ}|g||d…< t	|ƒ| _dS dS )a  Adds a new argument to the method. It is possible to add an argument at specified position
		by setting up the insert_before or insert_after argument name. By the way there are dedicated
		methods existing as aliases to an appropriate operation - see LadonMethodInfo:arg_insert_before()
		and LadonMethodInfo:arg_insert_after()
		Please, note, that only one of insert_before or insert_after parameter will be taken into account
		and insert_before is a high priority. It means that bypassing both parameters at a time
		will cause selection of insert_before behavior, insert_after will be ommited
		By default the argument will be added to the end of service method's arguments list
		Returns True if operation successfull, False otherwise
	
		@param arg_name: a name of an argument to add to a service method
		@param arg_type: an argument type defined for a service method
		@param arg_doc: a docuentation string(s) to add to a service method
		@param is_optional: specifies if an argument should be treated as optional or not at a service method
		@param insert_before: specifies a name of service method's argument before which the new argument should be added
		@param insert_after: specifies a name of service method's argument after which the new argument should be added
		@rtype: bool
		)rl   r)   rk   r"   Nr   TF)
Úhas_argr"   Ústrrq   rt   Úindexr|   r`   rw   r{   )	r   Úarg_nameÚarg_typeÚarg_docÚis_optionalÚinsert_beforeÚinsert_afterr…   Úatlistr
   r
   r   Úadd_argˆ  s$    



zLadonMethodInfo.add_argc             C   s   | j |||||dS )aL  
		An alias for LadonMethodInfo:add_arg() with the insert_before parameter specified as ref_arg_name
		Returns True if operation successfull, False otherwise
		
		@param ref_arg_name: name of service method's argument before which the new argument should be added
		@param arg_name: a name of an argument to add to a service method
		@param arg_type: an argument type defined for a service method
		@param arg_doc: a docuentation string(s) to add to a service method
		@param is_optional: specifies if an argument should be treated as optional or not at a service method
		@rtype: bool
		)r   )r’   )r   Úref_arg_namer‹   rŒ   r   rŽ   r
   r
   r   Úarg_insert_before¸  s    z!LadonMethodInfo.arg_insert_beforec             C   s   | j |||||dS )aJ  
		An alias for LadonMethodInfo:add_arg() with the insert_after parameter specified as ref_arg_name
		Returns True if operation successfull, False otherwise
		
		@param ref_arg_name: name of service method's argument after which the new argument should be added
		@param arg_name: a name of an argument to add to a service method
		@param arg_type: an argument type defined for a service method
		@param arg_doc: a docuentation string(s) to add to a service method
		@param is_optional: specifies if an argument should be treated as optional or not at a service method
		@rtype: bool
		)r   )r’   )r   r“   r‹   rŒ   r   rŽ   r
   r
   r   Úarg_insert_afterÆ  s    z LadonMethodInfo.arg_insert_afterc             C   sR   |   |¡rN| j| }| j |¡}| j|= | j|= t| jƒ}||= t|ƒ| _|S dS )a{  
		Removes an argument with the name specified from a registered arguments for current service method
		Returns removed argument representation or False if argument is not present in the method's arument list
		
		@param arg_name: name of a service method's argument to remove
		@rtype: dict { "doc" : [str], "name" : str, "optional" : bool, "type" : LadonType } or bool False
		F)rˆ   rq   rt   rŠ   r`   rw   r{   )r   r‹   Úargr…   r‘   r
   r
   r   Ú
remove_argÔ  s    



zLadonMethodInfo.remove_argc             C   s
   || j kS )z¦
		Checks if the argument with the name specified exists in the list of service method arguments
		
		@param arg_name: name of an argument to check
		@rtype: bool 
		)rq   )r   r‹   r
   r
   r   rˆ   ê  s    zLadonMethodInfo.has_argc             C   s   t  |  ¡ ¡S )N)rV   rW   rM   )r   r
   r
   r   rX   ó  s    zLadonMethodInfo.__str__)FNN)F)F)r   r1   rY   rZ   r   r)   rs   rM   r‡   r’   r”   r•   r—   rˆ   rX   r
   r
   r
   r   r^   »  s    &
0

	r^   c               C   s   t s
tƒ a t S )z§
	Fetch the singleton instance of LadonServiceCollection refered to as the
	global service collection.
	
	@rtype LadonServiceCollection The global service collection
	)Ú
global_lscr   r
   r
   r
   r   Úglobal_service_collectionù  s    	r™   )r%   r    ÚrerV   ro   rS   Z!mysite.ladon.exceptions.ladonizerZmysite.ladonr   Zmysite.ladon.types.ladontyper   Zmysite.ladon.typesr   Zmysite.ladon.types.typemanagerr   rp   Zchardet_py2r   r   ZchardetÚobjectr   r6   Úcompiler~   r‚   rƒ   r^   r˜   r™   r
   r
   r
   r   Ú<module>   s0   0
   


  =