what I'm basically trying to do is:

x = struct( size=4, order='var0', # <<< struct.__call__( size, order, **vars )

    var0 = bu32 # { 'var0': bu32, }


data = x() # read data from imported file
print data.var0 # should return a bu32 int

if data.__class__ == x: pass

# these next 2 should reference the same object
if data.__class__.__class__ == struct: pass
if x.__class__ == struct: pass

I believe it's possible, but I'm not sure how to do it...

so what does everything in the code above do exactly?

bu32() is a data function which reads a big-endian unsigned 32bit int or 0x00000000 from a file imported into my interface.

struct() is an advanced class which when called calls those functions in the order given to read the data from the file AND structure the data as the vars given.

x = struct()
x is a new sub-class returned by struct used for identifying particular data structures in your code.

as you can guess, sub-classing is something new to me, so I have no idea how to do this.

another way to represent this: (for those confused who might have an idea)
here's what's expected:

>>> type(x)
<class '__main__.struct'>
>>> data = x()
>>> type(data) # name changed
<class '__main__.x'>

thanks for any and all help :)

EDIT: I should mention I'm using portable python which is no later than version

At first sight, it seems that you are trying to do some unpythonic design. I'd like to have the code of class struct. Is it an ordinary user defined class or something else ? Also I don't understand this

data = x() # read data from imported file

If x is a class, the statement data = x() means that you are instantiating class x. This is very different from reading data from a file. Also, if you have a good design, you should be able to avoid testing the __class__ member of your instances.

In principle, a class instance can be a class. In fact every class in python is an instance of a metaclass (every python object is an instance), but it is unlikely that you really need this in your program.

that's just it... I don't exaclty have a solid code for struct()()

but I know what I want it to do:

we'll go a little more in depth and define a struct that represents a 3D bf32 vertex coord

vert = struct( 12, 'x,y,z',
    x = bf32,
    y = bf32,
    z = bf32

when reading this data from a file, simply call:

data = vert()

and when writing this data to a file:

vert( [1.0, 1.0, 1.0] )

structs can also be used in structs, as so:

facepoint = struct( 12, 'position',
    position = vert

but I don't know all about how every model format is structured,
so let's say you have some complex occurrence where you need to check the type of data supplied:

if data.__class__ == struct:
    value = data()
    if value.__class__ == vert:

so that's why I need .__class__.__class__ == struct and .__class__ == vert

so I'm asking how I could do this before I build the solid code for struct()()

I really didn't know how to ask the Q :P

re-reading your statement, I see you were confused about another aspect of struct()()

basically struct( size, order, **vars )( value=None )

size is the size of the data structure in the file's data (imported by UMC's interface (sorry I hadn't mentioned that earlier))

struct( 12, 't', t=bu32 )()
this reads 0x######## ################ from the file.

order is the order the **vars are read from the file (because Py2x dictionaries can't be ordered like what's needed)

**vars is of course the variables you specify for the structure.

as you can see in the above post, you can reverence a struct's variables as if it were a class (just like C++ behavior, which is applauded for ease of handling binary file data)

value applies to the R/W action taken on the file data

again, UMC has it's own file system, so it's interface isn't limited to the struct module for binary data.

You are trying to do something resembling the collections.namedtuple system. struct() will be a function which returns a class, as namedtuple() returns a new subclass of tuple.

Namedtuple's arguments are the name of the class and the fields name. In struct, you also have a type for each field and a size. I suppose that the size is the sum of the sizes of the item types, so it could be computed automatically. I suggest the following API

def struct(name, fields, types):
    """Return a new type.
    Could be a subtype of namedtuple(name, fields)
    Add class members:
        _field_types: a dictionary field name --> type
        _size: computed once at class creation

# for example

vert = struct('vert', 'x y z', (bf32, bf32, bf32))

# load from and dump to file with a pickle-like syntax:

v = vert.load(file=ifh)

w = vert([1.0, 1.0, 1.0])


load() would be a class method and dump() an instance method.

umm... I think I've gotten off topic trying to explain what struct does...
no, I can get struct()() working as I've displayed above...

what I CAN'T get working is...

all I'm asking is for a method to do this:

>>> data = vert()
>>> data.__class__ == vert
>>> data.__class__.__class__ == struct
>>> vert.__class__ == struct

Yes you can do that by subclassing type. Here is a simplified example

>>> class struct(type):
...  def __new__(meta, size, fields):
...   name = 'foo'
...   bases = (object,)
...   D = dict(fields = fields, size = size)
...   return type.__new__(meta, name, bases, D)
...  def __init__(meta, size, fields):
...   pass
>>> vert = struct(12, 'x y')
>>> vert.__class__
<class '__main__.struct'>
>>> data = vert()
>>> data.__class__
<class '__main__.foo'>
>>> data.__class__ == vert

However, your code would be better if it was more pythonic. For example isinstance(data, vert) is already better than using __class__. Also reading or writing to file through the __call__ method obscures your code. load() and dump() are pythonic.

Finally, a namedtuple-like implementation of your structs would probably be a good idea as named tuples are simple structures that everybody understands.

sweet, I knew there was a way ^_^
Grib strikes again! =D

thanks :)

heh isinstance() is a backend call though where __class__ is a direct reference...

it be more logical and slightly more performative to use __class__...
just tried 2 runs and __class__ is faster than isinstance():

>>> timeit.timeit(setup="class t: pass\nx=t()", stmt="x.__class__==t")
>>> timeit.timeit(setup="class t: pass\nx=t()", stmt="isinstance(x,t)")

>>> timeit.timeit(setup="class t: pass\nx=t()", stmt="x.__class__==t")
>>> timeit.timeit(setup="class t: pass\nx=t()", stmt="isinstance(x,t)")

as for the "file" in my case, that's rather a prettied up array('B',[]) which is the fastest, most memory-stable array type for handling file data.

bytearray() is the slowest.
dict() is the fastest, but it's out of control with memory... heh

also, all of my code for UMC 3.0 has a limitation standard on it's functions/classes:


not too much obscurity and allows for "automation" in scripts.
"automation" is a slang-word for a call-chain.
basically, it's a different way of coding using function calls w/o variables.

ease or preference... call it what you want... heh

these are UMC-Scripts which only USE python, they don't have to completely follow the standards ;)

noobs write UMC-Scripts so really you can expect them to use type(vert) == struct heh

hey grib, if you want more info on UMC's scripting standards, I'm working on a page here:
Note: make sure JS is enabled or the post won't display properly.

all I currently have though is a tree of functions and classes...
I intend to get more informative and give better examples as well as provide a template to work with.

this is documentation though, so it's not top priority until I have a solid backend for UMC 3.0

I'd like to read your code Tcll, but I don't have much time. I don't think it is a good idea to use a less pythonic statement in order to save 4e-8 seconds. Remember that

Premature optimization is the root of all evil (Knuth)

Also, meditate the zen of python by typing

>>> import this

For example if vert is a class of geometrical vertices, nobody understands that the expression vert([1.0, 1.0, 1.0]) means that you are storing the vertice somewhere in memory or on file.

all you need to know, when it comes to writing a UMC script, is that vert() (which you define in your script with struct()) invokes the struct and tells it to read the variables you've defined in the struct from the file.

the data read from the current file (as UMC's scripting interface is an advanced automated interruptable state machine) is stored in vert.x; vert.y; vert.z

or as an advancement (I've read a bit into python magic) x,y,z = vert

as far as UMC is concerned, the returned value of struct()() is the same thing as a python list, and will be displayed as such:

>>> print vert() # data being 0x 3F800000 3F800000 3F800000
[ 1.0, 1.0, 1.0 ]

though you can't entirely use it as a python list...
a struct is a fixed-size data "value".
(there's no append function or any of that)

in UMC3.0 (not 3.0a) you can even use a pointer system ref()/deref().
basically, think of the imported file as RAM data (which alot of files I work with are exactly that).

so these functions for the data are really just advanced data manipulation systems for files.
the standards they follow is something that'll be easy for noobs to use.

speaking for myself on that level, when I first built UMC3.0a I actually did not know how to use classes and had a very difficult time trying to understand them.
so UMC3.0a was first built and released with nothing but functions.
(the first build was built using GLUT and wasn't able to do much)

my first class was the view-matrix transformation class since I switched to pygame after freeglut.

I still follow those standards for UMC's frontend design, which allows me to manipulate the backend however I please.
(for example, the pointer system in 3.0)

so what can UMC boast if everything seems so complex?
think of Blender26 with 73% less code for it's scripts.
(that's about the best way to describe my goal)

as far as performance goes (such as __class__), that's all in the backend...
the frontend (what's supplied to UMC's scripts) is for noobs to use.
however, it's not limited to that, it'll ease up alot of work for pros as well :)

the frontend vs the backend can basically be described as 2 different languages...
the backend needs to be optimized for performance and logic
the frontend needs to be optimized for simplicity and wide support

I honestly don't care much about following pythonic standards...
if I was using python3 I might care a bit more.

nobody understands that the expression vert([1.0, 1.0, 1.0]) means that you are storing the vertice somewhere in memory or on file.

that's for the documentation to cover.
I'm defining my own standards, so documentation is necessary. ;)

I have no standards applied to UMC-libraries though :)
UMC-libraries actually have access to UMC's backend as well as it's shader interface.

for example, for an MCE *.schematic export of a model,
in 3.0, you'll be able to view your model as it's transformed into a minecraft statue with various solid blocks updated in real-time.

you'll also be able to play an animation and watch the blocks update like 3D pixels before exporting the current frame as a MCE *.schematic file.

the feature control over this display is done with a UMC library

so feel free to go all out pythonic if you like with it
you can even use the scripting frontend in your library :)

if you want to know how much thought I've put into UMC, the prefix UGE stands for Universal Game Editor, which also counts as a logical recompiler for converting something like a Wii DOL/REL to a Windows EXE/DLL.

don't forget I'm autistic :P
my brain sees extreme complexity as simple and visualizes logic in many various ways.
(I have a severely overactive imagination which I have no control over)

1 thing makes me think of 100 things in a millisecond.
a negative effect is I can very easily overthink things and in return have to sort myself out, making me extremely slow to understand anything.

I don't permit myself to drive over it... heh
I'd easily cause the next biggest car accident if I did due to constant distractions.

sorry about going into a rant...
like I just said... I have autism, and 1 simple thing can set it off like that. :P

It's OK. Only remember that using python standards help you share your code with other python programmers !

ok yea, THAT I can agree with ;)

hey grib, would you know how to get the variable name struct() is applied to??

>>> class struct(type):
...  def __new__(meta, size, order, vars):
...   name = # var name here
...   bases = (object,)
...   D = dict(size=size, order=order, vars=vars)
...   return type.__new__(meta, name, bases, D)
...  def __init__(meta, size, order, vars):
...   pass

example of expected behavior:

>>> var = struct(12,'x,y',x=bf32,y=bf32)
>>> var
<class "__main__.var">

I should mention this is for debug purposes.
(because <class "__main__.foo"> would confuse whoever's writing the script)

also, how do I put an init code in:

return type.__new__(meta, name, bases, D)

because this is the code that actually reads the ordered vars from the file data.
(that's the purpose of the order) blah

The pythonic solution to name the class the way you want in such a context is to pass the class name as argument. I mean have a syntax such as

var = struct("var", 12,'x,y',x=bf32,y=bf32)

This is the way the namedtuple constructor works for example.

Actually, it is possible to extract the variable name at run time. I wrote a code snippet long ago about this. It involves examining the bytecode at run time to find the variable name. Needless to say it is a dirty hack, and it may not work with all versions or implementations of python. It would probably be a non trivial task to turn this into a robust tool.

But you could try to use it if it still works :)

yea, but noobs won't know how to do that, and it's more unneeded code for them to write...

how can I get "var" from inside the struct() class??

idc about pythonic... I can clear up any mis-conceptions wherever needed, with a little commenting...

I'm trying to make my functions/classes as simple to use as possible
(you don't need to supply the var name, it should do that automagically)

EDIT: should I use a decorator supplied with the code calls??
(similar to what I did for something on my IDE)
^ http://lh4.ggpht.com/-WDSklcFeKjs/U2HHfw17IDI/AAAAAAAAGbA/-j4z4g-QiBE/s801/UMC_SIDE_progress_13.PNG

I should mention the struct class itself is it's own data type as you define it.
(it should work very similar to C structs)

you can use this with my pointer system.

weight = struct( 8, 'ptr,W',
    ptr = bu32
    W = bf32

Wptr = bu32() #wherever location in the file
Wdata = deref( weight, Wptr ) #relative to the current file (not physical RAM)

print Wdata.W

I should also mention the file data I'm working with is RAM data.
that's where my pointer system really helps, and it's specifically necessary for this format:
especially for calculating the pointers located in the relocation table at the end of the file.

Just in case anyone's questioning:
Why am I doing this sort of low level work in python of all languages?

I have a few reasons:
1: The amount of code needed to pull this off in C would be outrageously larger by thousands of lines. (and FASM is even worse)
2: To prove a few points to quite a few people that python is alot more powerful than what people expect it to be.
3: Because with Python, I don't need to worry about the hastle of compiling my code, and can therefore release it as Open Source as I want it.
4: Because Python has perhaps the simplest syntax of all programming languages, and being dynamic makes it all the more simpler.

I have more reasons, but those are my best.

Those are very good reasons. You probably know that python can also generate C code.

Here is my code, using my code snippet (module kernilis.withvarnames) This is the version at the bottom of the snippet thread. Store it as whatever module name you prefer

#!/usr/bin/env python
# -*-coding: utf8-*-
from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

from kernilis.withvarnames import with_varnames

class struct(type):
    def __new__(meta, size, order, **vars):
        varnames = vars.pop('varnames', ())
        if len(varnames) != 1:
            raise TypeError("struct type may be defined only "
                "through a statement 'name = struct(...)'")
        name = varnames[0] # var name here
        bases = (object,)
        D = dict(size=size, order=order, vars=vars)
        return (type.__new__(meta, name, bases, D),)

    def __init__(meta, size, order, **vars):
        print('hello from __init__() method.', vars)

if __name__ == '__main__':
    bf32 = "dummy"
    var = struct(12, 'x,y', x=bf32, y=bf32)

My output with python 2.7 in linux (same output with 3.4):

19:51 488923] python tcll.py
hello from __init__() method. {'y': u'dummy', 'x': u'dummy'}
<class '__main__.var'>

You probably know that python can also generate C code.

actually, I didn't know that... lol

could I get a link to your snippet page plox?? :)
I'm just interested to know how you did that... heh
though I'm seeing you're using a decorator

though this could be bad:

varnames = vars.pop('varnames', ())


blend format uses actual var names in it's files...

but for a similar issue, who's to say that someone won't create:

struct( -1, 'varnames,pointers',
    varnames = array(string(u8(big=endian))), # this is a pending idea
    pointers = array(bu32,0)

array is my custom class:
array(settings)(R/W) # call to read, supply data to write

The snippet is here. If you don't like the name varnames, change the snippet to have
_tcll_own_varnames__ or some other unlikely name. You can also use
a singleton python object, for exemple

_VARNAMES = object()

instead of the string object "varnames". It may actually be an improvement of the code snippet.

haha, you're welcome :3

or perhapse store what's needed in a sys.modules[] namespace:

sys.modules["kernilis._VARNAMES"] = object()

I'm doing this for my GUI widgets, and have done it before with my IDE.

I also like using it over import due to local namespace, and it keeps the module in it's namespace:
(that way you don't mix and match, which I could assume takes more memory)

V = sys.modules['VIEWER'] # UMC's viewer

of course V takes extra memory... heh
but multiple calls to sys.modules['VIEWER'] eats performance

and I'm saying that with a lack of knowledge on how the interpreter works... haha
it's just logical knowledge made up by visual representations in my head. :P

yea I like to visualize the logic in action,
the best representations are visual pulses through wires.

btw, I like how yours follows almost exactly the same methods as mine:

import sys, inspect
from functools import wraps

from PyQt4 import QtGui

sys.modules['UGE_EXTERNAL'] = 'UGE'

def trace(func):
    def wrapper( *args, **kwargs ):
        called = func( *args,**kwargs ) # needed to call the function first to update the defaults

        if sys.modules['UGE_EXTERNAL'] == 'UMC_SIDE': # run this code if the external is the IDE
            C = sys.modules['UMC_SIDE_GO_CODE'] # [ [[ [ 'func', column ], ... ], index ], ... ]
            frame = inspect.currentframe()
                fbk = frame.f_back

                # for UMC (or UGE), test the function name
                name = func.__name__
                if name in ['__init__','__call__']: name = args[0].__class__.__name__
                # ^ this always means a UGE data type, so update the name with the class

                # get the column and text of the called function
                columns,index = C[fbk.f_lineno][name]
                text, column = columns[index]

                    # specific for UGE data types
                    offset = args[0]._addr
                    size = args[0]._size
                    value = args[0].value

                    print text
                    color = hash(text if name in ['_SUB_STRUCT'] else name.replace('_','(')+')')
                    color = QtGui.QColor((color>>16)&255, (color>>8)&255, color&255, 128)

                    # append data to return
                    sys.modules['UMC_SIDE_TREE_CODE'].append(( text, fbk.f_lineno, column, value, offset, size, color ))
                except AttributeError:
                    print name
                    # append data to return
                    sys.modules['UMC_SIDE_TREE_CODE'].append(( text, fbk.f_lineno, column ))

                if index+1 == len(columns):
                    C[fbk.f_lineno][name][1] = 0
                    C[fbk.f_lineno][name][1] += 1

            finally: del frame # avert possible memory leak

        return called
    return wrapper

this is what I'd used for validating UMC data-types for UMC_SIDE's hex viewer:

It occurred to me that you could replace

varnames = vars.pop('varnames', ())


varnames = vars.pop('varnames', ("<anonymous struct>",))

in my previous code. It would allow creating struct having a descriptive default name without an assignment statement.

You may also need to change the arguments to D = dict(...) to fill the class body properly. For example, it may be

vars['size'] = size
vars['order'] = order
D = dict(vars)

I've just reformatted your code so it's easier to understand: (object-wize)

class VarnamesError (Exception ):

def assignment_varnames (code ,lasti ):
  """Extract variable names from a statement of the form
  x, y, z = function(...)
  in a code objet @code where @lasti is the index of the
  CPython bytecode instruction where the function is called.
  import opcode
  call, unpack, storef, storen = (
    opcode.opmap[s] for s in ("CALL_FUNCTION","UNPACK_SEQUENCE","STORE_FAST","STORE_NAME")
  errmsg = "simple assignment syntax 'x, y, z = ...' expected"
  varnames = []
  co = code.co_code
  i = lasti
  if byte_ord( co[i] ) != call: raise VarnamesError (errmsg )
  i +=3
  if byte_ord( co[i] )==unpack :
    nvars = byte_ord( co[i+1] ) + byte_ord( co[i+2] ) * 256
    i +=3
  else: nvars = 1
  for j in range(nvars) :
    k = byte_ord( co[i] )
    oparg = byte_ord( co[i+1] ) + byte_ord( co[i+2] ) * 256
    if k==storef: varnames.append( code.co_varnames[ oparg ] )
    elif k==storen: varnames.append( code.co_names[ oparg ] )
    else: raise VarnamesError( errmsg )
    i += 3 
  return varnames

I really hate pythonic standards...
your original format was a complete mess... just sayin

with my formatting, you can see the progression layers as the interpreter goes through everything.

anyways... I'll fix up your code and use something better than passing the variables an included keyword with a dynamic dictionary :)

I think what would be better is if your decorator could behave more like a class, injecting the variables as a first or second argument:

def func(vars,options=None) #not sure if the None is needed
    print vars
    print options

>>> var = func(1)

what I mean by "second argument":

class dummy:
    def func(this,vars,options=None) #not sure if the None is needed
        print vars
        print options


What we could do is to push the varnames tuple on the top of an external stack (or a collections.deque) before calling the function,
and pop it out of the stack when f returns. Something along the line of

def with_varnames(func):
    def wrapper(...):
        names = assignment_varnames(...)
            return f(...)
    return wrapper

_stack = collections.deque()

def varnames():
    return _stack[0]

def f(this, x, y):
    print varnames()
    print x, y

This would leave the function's argument list intact ! (and still allow reentrant calls, etc). The name of the decorator could could be changed to @uses_varnames which would be closer to what the function is actually doing.

haha go figure, I was actually just considering an external global var, but I think I like that function idea better :)
(it's more solid and less error prone) :P

but instead of locally defining varnames() like you're doing, you should use some python magic to supply it with the wrapped function globals :)

or just supply it with your module and import it where needed :P

actually, here's a better version of my initial idea:

>>> def f(): print v

>>> f.func_globals['v'] = 1
>>> f()

I think a module usesvarnames can contain both the decorator, the stack variable and the function varnames(). Nothing else is needed near the wrapped function. Code could look like

import usesvarnames as vn

def f(u, v, w):
    x = vn.varnames()

Edit: the thing to remember from my snippet is that the function must return a sequence of n items, with n being the number of variables. This is to allow the assignment statement when there is more than one variable.
Edit2: This should probably be changed when there is only one variable, so that the function could use

return val

when there is 1 variable and

return val1, val2, val3

when there are 3 variables. We may be on our way to a version 0.5 of this decorator.

well... here's my failed attempt at a simpler replication of your code... heh

import sys, inspect, opcode
from functools import wraps

class VarnamesError (Exception ): pass
if sys.version_info >=(3,): ord = lambda x :x
def with_varnames(func):
    """with_varnames(function) -> decorated function

This decorator allows a function to extract variable names
from the line of code where it is called, and create
values for these variables which depend on their names.

@function : a function which accepts a keyword argument 'varnames'
            (supposed to be a sequence) and returns a sequence of
            values of the same length.

example code:

# a values creation function, decorated with with_varnames

def my_values(varnames = ()):
    return [name+str(i) for i, name in enumerate(varnames)]

# first way to call my_values: pass all the arguments
values = my_values(varnames = ("u", "v", "w"))

# second way: through an assignment expression with the variable names on the left
x, y, z = my_values()
print(x, y, z)"""

    def wrapper( *args, **kwargs ):
        frame = inspect.currentframe()
            fbk = frame.f_back
            code,lasti = frame.f_code, frame.f_lasti 
        finally: del frame # avert possible memory leak

        call, unpack, storef, storen = (
            opcode.opmap[s] for s in ("CALL_FUNCTION","UNPACK_SEQUENCE","STORE_FAST","STORE_NAME")
        errmsg = "simple assignment syntax 'x, y, z = ...' expected"
        varnames = []; co = code.co_code; i = lasti

        if ord( co[i] ) != call: raise VarnamesError (errmsg )
        if ord( co[i] )==unpack :
            nvars = ord( co[i+1] ) + ord( co[i+2] ) * 256
        else: nvars = 1

        for j in range(nvars):
            k = ord( co[i] ); oparg = ord( co[i+1] ) + ord( co[i+2] ) * 256
            if k==storef: varnames.append( code.co_varnames[ oparg ] )
            elif k==storen: varnames.append( code.co_names[ oparg ] )
            else: raise VarnamesError( errmsg )

        func.func_globals['varnames'] = varnames
        return func( *args, **kwargs )
    return wrapper

failed because:

>>> @with_varnames
def f(): print varnames; return (0,1,2)

>>> a,b,c = f()

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    a,b,c = f()
  File "\home\tcll\Desktop\UMC\UMC_v3.0\API\externals.py", line 21, in wrapper
    if ord( co[i] ) != call: raise VarnamesError (errmsg )
VarnamesError: simple assignment syntax 'x, y, z = ...' expected

I lack knowledge to understand what's happening here... heh

Ok, I'll write the new version. Why do you want to inline the helper function in the wrapper ? It obfuscates things. Let the bytecode apart.
I'm working on it.

splitting things up like that creates logic links which confuse me personally...

not to mention it's actually a little slower due to a separate function call...

and when you're dealing with a wrapper on something called often (especially with conversive cases), it's best to have things be as simple as possible.

note, when I say "simple" I realize that the way I understand things is different from everyone else's layout, which I like to call "dirty".

so when I say "simple", I'm speaking for the byte-code converted and ran by the interpreter.
(it's easier to understand if you can picture the simplistic execution of the byte-code)

no I won't say everyone has the ability to literally see logic as if it was a 3D object... I do believe that's a trait of my autism...

but if you want to have a good code, try to code from the byte-code standpoint rather than physical text.

so yea, I want to in-line it because it's easier for me to understand.

do take note of the white-space in my remake of your code

I type code like I type posts

so when I switch topics or go off to separate tangents (as par my autism),
I create a space in my posts to show that off, and even larger for bigger break-offs.

now you can follow that in the code I type... it's quite simple really... heh

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.