#!/www/python/bin/python

"""check_data

Read in an Grouch schema and use it to type-check a Python object graph.
The source of the data is currently limited to either a ZODB database or
a pickle file.
"""

# created 2001/08/17, Greg Ward (from MEMS Exchange-specific check_db)

__revision__ = "$Id: check_data 21022 2003-03-10 20:02:59Z gward $"


import sys, os
import getopt
from cPickle import load

from grouch.context import TypecheckContext
from grouch.script_util import announce, warn


def get_root_object (data_source, format, storage):
    """Open the data source according to format and storage.
    Return the root of the object graph.
    """
    if format == "pickle":
        file = open(data_source)
        return load(file)

    elif format == "zodb":
        import ZODB
        from ZODB import DB

        if storage == "file":
            from ZODB.FileStorage import FileStorage
            storage = FileStorage(data_source)
        elif storage == "client":
            from ZEO.ClientStorage import ClientStorage
            (host, port) = data_source.split(":")
            storage = ClientStorage((host, port), var="/tmp")

        db = DB(storage)
        conn = db.open()
        return conn.root()

    else:
        raise RuntimeError, \
              ("this can't happen (format = %r, storage = %r)"
               % (format, storage))

# get_root_object ()


def main ():
    global VERBOSITY

    prog = os.path.basename(sys.argv[0])
    args = sys.argv[1:]

    usage = """\
usage: %s [options] schema_file data_source
options:
  -f FMT, --format=FMT
     specify the format of the data source (currently: \"pickle\", \"zodb\")
  -s STG, --storage=STG
     specify the storage of a ZODB (currently: \"file\", \"client\")
  -n NAME, --root-name=NAME
     set the name of the root object (to make error messages more
     readable) [default: "root"]
  -t TYPE, --root-type=TYPE
     set the type of the root object [default: the type of the root
     isn't enforced, only the type of objects reachable from it]

schema_file must be a pickled Grouch schema as created by gen_schema.

data_source depends on FMT and STG.  For \"pickle\" and ZODB FileStorage,
it should just be a filename.  For ZODB ClientStorage (ZEO),
it should be the host:port of the ZEO server.

""" % prog

    try:
        (opts, args) = getopt.getopt(args, "f:s:n:t:vq",
                                     ["format=",
                                      "storage=",
                                      "root-name=",
                                      "root-type=",])
    except getopt.error, msg:
        sys.exit(usage + str(msg))

    format = "pickle"
    storage = None                      # only applicable for ZODB
    root_name = "root"
    root_type = None
    VERBOSITY = 1

    for (opt, val) in opts:
        if opt in ("-f", "--format"):
            format = val
        elif opt in ("-s", "--storage"):
            storage = val
        elif opt in ("-n", "--root-name"):
            root_name = val
        elif opt in ("-t", "--root-type"):
            root_type = val
        elif opt == "-v":
            VERBOSITY += 1
        elif opt == "-q":
            VERBOSITY = 0

    if format not in ("pickle", "zodb"):
        sys.exit(usage + "bad format %r: must be pickle or zodb" % format)
    if format != "zodb" and storage:
        warn("storage ignored for non-ZODB formats")
        storage = None
    if format == "zodb" and storage is None:
        warn("using default file storage for ZODB database")
        storage = "file"

    if len(args) != 2:
        sys.exit(usage + "wrong number of arguments")

    (schema_filename, data_source) = args

    announce("Loading schema...")
    file = open(schema_filename)
    schema = load(file)
    announce("\n")

    if root_type:
        try:
            root_type = schema.parse_type(root_type)
        except ValueError, err:
            sys.exit("invalid root type: %s" % err)

    root_object = get_root_object(data_source, format, storage)
    if format == "zodb":
        mode = "zodb"
    else:
        mode = "memory"
    context = TypecheckContext(mode=mode, start_name=root_name)

    announce("Checking object graph...\n")
    if root_type:
        root_type.check_value(root_object, context)
    else:
        schema.check_value(root_object, context)

    announce("found %d error(s)\n" % context.num_errors())
    sys.exit(context.num_errors() > 0)

# main ()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        sys.exit("interrupted")
