home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Languages / python / PyObjC-0.47-MIHS / pyobjc-0.47-src / Demo / ObjC / PyServices / README < prev   
Encoding:
Text File  |  1996-10-10  |  9.2 KB  |  187 lines

  1. PyServices
  2. ----------
  3.  
  4. A python implementation of NEXTSTEP services.
  5.  
  6. Written by Bill Bumgarner <bbum@friday.com> using the ObjC module for Python.  Please report all bugs, patches, and requests to <bbum@friday.com>.
  7.  
  8. PLEASE see the TODO section before adding any services!!!!
  9.  
  10. Demonstration
  11. -------------
  12.  
  13. Before invoking this demo, one must build a custom python binary (or all of the modules as dynamically loadable extensions).  Once the binary/modules are built, cd into Demo/ObjC/PyServices:
  14.  
  15. 1. copy PyServices.services to ~/Library/Services/
  16.  
  17. 2. make_services -v | open
  18.    This will recompile all of the NEXTSTEP services for your account. The 
  19.    -v flag will cause a summary of all services to be spewed to stdout.
  20.    Search for 'PSDaemon' in the result;  you should see several services
  21.    with the port 'PyServices'.
  22.    
  23.    Quit and restart Edit.app;  the services menu should now include a
  24.    'PyServices/' entry with several actions.  They will not respond 
  25.    until you start the daemon by hand [unless you go for a full install].
  26.  
  27. 3. ../../../python pyservicesdaemon.py --lib-path . \
  28.                 --services-path PyServicesHandlers
  29.    [That should be all one line].  The above will start the services daemon.
  30.  
  31. 4. Go to Edit.  Create a new document.  Copy the following URL into it:
  32.    http://www.friday.com/hello.txt
  33.    Select the URL.  Select Services->PyServices->Expand URL.
  34.  
  35. 5. The daemon does not respond to Ctrl-C.  To kill, either Ctrl-z and 
  36.    kill %, or select some text and select PyServices->Kill Daemon.
  37.    Kill Daemon actually does something very interesting-- it causes 
  38.    the daemon to fork and invoke the kill from the child.  That way, 
  39.    the parent successfully returns from the service handler before
  40.    dieing. The child doesn't currently but should really sleep at
  41.    least a second before killing the parent.
  42.  
  43. Full Installation
  44. -----------------
  45.  
  46. If you have decided that having python implemented services really is a very useful thing, then this is how you can achieve a full installation that launches automatically when you log in.  If you want the installation to be available for everyone, substitute /LocalLibrary/ for all occurrences of ~/Library:
  47.  
  48. 1. Make the custom python binary or modules.  I would suggest building 
  49.    a custom, statically linked python executable with the name 'pyobjc'.
  50.    Ie; from the root level after the 'make -f Misc/Makefile.pre.in boot',
  51.    do:
  52.    
  53.    make static TARGET=pyobjc
  54.    
  55.    This will yield a binary named pyobjc that is compatible with
  56.    the installed python 1.4 library, but has all of the stuff from
  57.    the PyObjc project statically linked in.
  58.    
  59.    Copy 'pyobjc' to /usr/local/bin/
  60.  
  61. 2. cd Demo/ObjC/PyServices
  62.  
  63. 3. Copy PyServices.services to ~/Library/Services/ and 'make_services' 
  64.    (see Demo section).
  65.  
  66. 4. Make a home for everything:
  67.    mkdirs ~/Library/PyServices/lib
  68.  
  69. 5. Copy the python library files:
  70.    cp -r PyServicesPackage ~/Library/PyServices/lib
  71.  
  72. 6. Copy the standard handlers:
  73.    cp -r PyServicesHandlers ~/Library/PyServices
  74.  
  75. 7. Compile all handlers and lib files:
  76.    python -c 'import os; import compileall; \
  77.       compileall.compile_dir(os.path.expanduser("~/Library/PyServices"))'
  78.    
  79.    That's all one line... No point in compiling pyservicesdaemon.py;  
  80.    the python interpreter won't find it, anyway.  Currently, there is
  81.    no way to have the main file compiled.  Hence, for scripts that are
  82.    invoked often, it is optimal to implement as much of the script as
  83.    possible in imported files!  [hint: CGI scripts]
  84.    
  85. 8. Copy the daemon:
  86.    cp pyservicesdaemon.py ~/Library/PyServices
  87.  
  88. 9. cd ~ [or some other directory than anywhere within the pyobjc project]
  89.  
  90. 10. Invoke the daemon:
  91.    ~/Library/PyServices/pyservicesdaemon.py
  92.  
  93. 11. Test it.
  94.  
  95. 12. If it works [it should]-- add the daemon to your LaunchPaths:
  96.    Add it to Workspace's LaunchPaths default.  Mine is currently [all one line]:
  97.    
  98.    Workspace LaunchPaths /LocalApps/TickleServices.app/TickleServer.daemon;~/Unix/bin/colorcursor.pl;~/Unix/bin/colortextbar.pl;~/Library/PyServices/pyservicesdaemon.py
  99.  
  100. Customizations
  101. --------------
  102.  
  103. There are two command line switches that can be used to customize where pyservicesdaemon.py looks for services implementations and library files.  Both can be specified any number of times and the order will determine the order in which the various directories will be scanned for library files and services implementations.
  104.  
  105. --lib-path directory:       add directory to sys.path
  106. --services-path directory:  search directory for services implementations
  107.  
  108. How the daemon finds a services implementation
  109. ----------------------------------------------
  110.  
  111. The file PyServices.services defines all of the current services implemented by the daemon.  Each service has a UserData field that is passed to the daemon whenever a service is invoked.  Compare the UserData field to the structure of the PyServicesHandler directory;  the UserData field specifies the path to the Python file that implements the named service!  In the future, the UserData field will be able to contain more than just the path.
  112.  
  113. Upon receipt of a service request, the daemon looks in each of the directories specified by the --services-path directive for a python file with the same name & relative path (below the handler directory) as that specified by the UserData field.  It is smart enough to load the compiled version as long as it is newer than the non-compiled version.
  114.  
  115. By default, the paths ~/Library/PyServices/PyServicesHandlers and /LocalLibrary/PyServices/PyServicesHandlers are scanned.
  116.  
  117. Any support packages can be placed in the ~/Library/PyServices/lib directory-- either as a ni based package (see PyServicesPackage and how it is referred to in the various PSDaemon/*.py services implementations) or as a plain old imported module.
  118.  
  119. Adding New Services
  120. -------------------
  121.  
  122. Adding new services is easy, though not nearly as automatic as it should be.
  123.  
  124. 1. Create a services description:
  125.  
  126. ##
  127. ## Hello World Service
  128. ##
  129. Message: forwardService
  130. Deactivate Requestor: NO
  131. Port: PyServices
  132. Menu Item: Demo/Hello World
  133. Timeout: 10000
  134. User Data: Demo/Hello World
  135. Send Type: NeXT plain ascii pasteboard type
  136. Return Type: NeXT plain ascii pasteboard type
  137.  
  138.    Save the above to-- say-- ~/Library/Services/Demo.services
  139.  
  140. 2. Update system servics:
  141.    make_services
  142.  
  143. 3. make the Demo services implementation directory:
  144.    mkdir ~/Library/PyServices/PyServicesHandlers/Demo
  145.  
  146. 4. fill in the implementation -- save the following code in the
  147.    directory from 3 as HelloWorld.py:
  148. ## -- cut from here --
  149. import pasteboard
  150.  
  151. def servicesHandler(handler, pb, userData):
  152.    pasteboard.write_string_type(pb, 'Hello, World!', 'NXAsciiPboardType')
  153. ## -- to here --
  154.  
  155. 5. Quit and Restart Edit [to update the services menu item].  Create a new
  156.    window.  Type a bit of garbage into it.  Select garbage.
  157.    Services->Demo->Hello World
  158.  
  159. Additional Notes
  160. ----------------
  161.  
  162. PyServices->Clear Cache  removes all handler entries from the handler cache in wtihin the handler dispatcher.  All subsequent services requests will cause the corresponding services python code to be reloaded.  This is extremely handy when developping new services.
  163.  
  164. The first argument to all services handler functions is the handler that dispatched the service.  This handler has some additional API and behaviour that you might find useful (and is not immediately obvious from the examples):
  165.  
  166. By importing the PyServicesHandler module from the PyServicesPackage package, you will have access to PyServicesHandler.VisibleError.  Raising an exception of type PyServicesHandler.VisibleError will cause the exception's value to be used as the error string for the original service request.  See PSDaemon/Test.py for an example.
  167.  
  168. The method getHandlerForService() takes a single argument that is typically the UserData field's value.  If one desires to have several services implemented by a single handler, one can simply provide stub handlers that invoke handler.getHandlerForService() with the path (relative to one of the paths in servicesPaths.
  169.  
  170. The method forkAndInvoke() takes a single function as an argument.  The function takes a single argument-- the process id of the parent.  forkAndInvoke() can be used to invoke long running processes or to implement services that invoke other services.  Since services requests are serialized-- ie; only one service can be handled at a time-- any service that invokes other services or service-like mechanisms must not do so from the parent process.
  171.  
  172. The servicesPaths attributes contains a list of all the paths upon which the dispatcher will search for services handlers.
  173.  
  174. The deamon attribute contains a reference to the PyServicesDaemon object that 'owns' the handler instance.  There really isn't much to the PyServicesDaemon class.
  175.  
  176. Because of the way the cache works, services can easily save 'state' across calls by accumulating said state within the handler's module's namespace.  It is not possible to add variables to the handler-- unless you really know what you are doing.
  177.  
  178. TODO
  179. ----
  180.  
  181. - provide automatic mechanism for compiling and installing the .services file
  182.  
  183. - create UI for editing services (see TickleServices).
  184.  
  185. - add more PSDaemon/ services for analyzing the state of the server
  186.  
  187. - add package support to services [such that a set of services may have a designated initializer that is automatically invoked when any of the services within the package is referenced].