Aimy

Core Basics

This file is mainly examples that I hope will help people better-understand how to use the core library.

Tree Structure Basics

The core library's basic principles are division of labor and inheritance of functionality and properties across objects.

To facilitate this, core-based objects (in particular, objects used as "services") should be organized into a single tree with one root object. The core_basic class provides linkage between objects in this tree by way of the core(), root(), and owner() methods.

import core
c = core.Core()                   # the core
r = core.core_basic({'core':c})  # root object
o = core.core_basic({'owner':r}) # an object


# NOTE: o's owner is the root object
r
o.owner

# NOTE: o "inherits" r's core property
c
r.core
o.core

# NOTE: o2 searches upward for the root object
o2 = core.core_basic({'owner':o})
o2.root

Object Configuration

Because the core_basic class is so limited, the next example will use core.Object to demonstrate configuration principles despite that the config() method is defined in core_basic.

Nearly all core-based configuration is the same, though some classes do extend config() if their nature implies alternate configuration schemes.

# Configuration is basically by dict object.
o = core.Object({'foo':'bar'})
o.config()

# kwargs always override any conf argument keys
o = core.Object({'foo':'bar'}, foo='Car')
o.config()

# Config accepts json or dict representation files.
core.util.fwrite('temp.conf',{'foo':'foofoo!'})
o = core.Object('temp.conf')
o.config()

# kwargs ALWAYS override the conf argument
o = core.Object('temp.conf', foo='BABAR!')
o.config()

ORISC - Individual Components in the Object Tree

One of the main objectives of the core package is to share time between clients, servers, and other types of object that require periodic passes through a "main loop" kind of function without the need for threading. ORISC objects facilitate this by acting as receivers for calls to such a main loop function.

Override ORISC's io() method to handle any action that needs to be handled periodically.

The run() method loops calling io(). It does not stop until told to. Use Ctrl-C to stop the loop if necessary.

Here are a couple of examples demonstrating the practical (or, in this case impractical) use of an ORISC object.

# import the core package, which defines ORISC
import core

# create a class called Loopy, based on ORISC
class Loopy (core.ORISC):
  
  # The io() method will be called repeatedly
  # when Loopy is running.
  def io(self):
    try:
      if self.loops > 0:
        print "I'm Loopy!"
        self.loops = self.loops-1
      else:
        print "I'm out of loops :-/"
        self.shutdown()
    except:
      self.loops = 5

# Create a Loopy and run it.
o = Loopy()
o.run()
My python interpreter doesn't like indented code copied from the syntax hilighter. If you're experiencing the same problem, click this link for some code that you can copy/paste into the interpreter.
Raw Code

Alternately, you can use the start() method to run Loopy in a new thread. The stop() method stops the run() loop by setting the "active" property to false, then exits the thread.

Be aware that sometimes when working with threads interpreter output can look a bit strange. Also, there may be times when a short "sleep" is required between operations; it's not necessary if you're executing the code line by line, but I've added a call to time.sleep() in at least one place below to prevent errors for those who copy/paste the whole example at once.

# Create a new Loopy since the old one's loops are
# already exhausted.
o = Loopy()

# Start the Loopy
o.start()

#
# You have to wait a bit here so that the
# Loopy thread gets a chance to exit!
#
import time
time.sleep(0.1)


# now try adding some more loops
o.loops = 3

# Remember, running out of loops causes Loopy to
# shut down (which stops the object and exists the
# thread) so we'll have to start() again.
o.start()

When debugging (or when learning the system by example) it can be really useful to call the io() method directly.

# Create a new Loopy since the old one's loops are
# already exhausted.
o = Loopy()

# Give Loopy some loops.
o.loops = 11

# Let Loopy do its thing one time...
o.io()

# ...and again...
o.io()

# ...Go for it, Loopy!
o.run()

The Core Class

The main purpose of the core package's structure is to allow projects to reload while running without losing connections (such as, socket connections). One core.Core object can be created to hold a full tree structure containing all other objects that need to survive a runtime reload.

The Core class constructor will typically receive the file path to a config file, but it can also accept a direct type.

Here's an example of how to run Loopy from within a core object.

#
# If you've haven't already done so, import core and
# paste the Loopy class code first
#

# Once core is imported and Loopy defined in the
# interpreter, create and start the Core object.
c = core.Core(root=Loopy)
c.start()

Core Facts

Here are a few more interesting things to note about Core:

import core

#
# 1:
# Any object that implements the ORISC class methods
# can be a root object (though usually a root object
# will be based on core.service.Service so that it 
# can hold additional "child" service items).
#
c = core.Core(root=core.ORISC)
c.start()
c.root

# ORISC PROPERTIES
c.running
c.threaded
c.active

# shutdown calls both close and stop
c.shutdown()
c.running
c.threaded
c.active

#
# 2:
# There can be only one Core object
#
c2 = core.Core(root=core.ORISC)


#
# 3:
# Be sure to call shutdown on the old core before 
# you Nullify it, or the thread will hold the active
# Core object open and you won't be able to shut it
# down without exiting the interpreter.
#
c.shutdown()

c = None
c3 = core.Core(root=core.ORISC)
c3.running
c3.threaded
c3.active


#
# 3-B:
# Though it can be configured differently, the root
# object by default is neither running nor threaded.
# It simply receives calls to io() from its core.
# 
c3.start()

c3.running
c3.root.running
c3.threaded
c3.root.threaded

# However, the root object will be active since core,
# when it's opened, calls root's open() method.
c3.active
c3.root.active


#
# 3-C:
# When the Core object is closed, the root object is
# released from memory. Note that calling stop() does
# not release the root; root is simply denied calls 
# to its io() method, rendering it active but inert.
#
c3.stop()
c3.root

# After closing the Core, its root is released.
c3.close()
c3.root