Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: alexgurrola <[email protected]>
  • Loading branch information
alexgurrola committed Feb 21, 2018
1 parent 59b8947 commit 95b0b93
Show file tree
Hide file tree
Showing 22 changed files with 490 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,6 @@ ENV/

# mypy
.mypy_cache/

# IDE Stuff
.idea
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Include the license file
include LICENSE

# Include the data files
recursive-include data *
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
snakelet
========

Snakelet is a Schema-less ORM-like system built in pure Python to reduce
redundancy with Mongo Native Drivers and Object Management. This package
contains a very simple implementation and will need to be expanded upon
largely to remain relevant.
Empty file added contrib/.gitkeep
Empty file.
Empty file added docs/.gitkeep
Empty file.
22 changes: 22 additions & 0 deletions requirements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
def main():
# settings
requirements_file = 'requirements.txt'

# read
with open(requirements_file, 'r') as requirements:
content = requirements.read().splitlines()

# sort content
content = list(set(content))
content.sort(key=lambda y: y.lower())
content = '\n'.join(content)
print(content)


if __name__ == '__main__':
import plac

try:
plac.call(main)
except KeyboardInterrupt:
print('\nGoodbye!')
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plac==0.9.6
pymongo==3.6.0
setuptools==36.5.0
5 changes: 5 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[bdist_wheel]
# This flag says that the code is written to work on both Python 2 and Python
# 3. If at all possible, it is good practice to do this. If you cannot, you
# will need to generate wheels for each Python version that you support.
universal=0
117 changes: 117 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""A setuptools based setup module.
See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
"""

# Always prefer setuptools over distutils
from setuptools import setup, find_packages

# To use a consistent encoding
from codecs import open
from os import path

here = path.abspath(path.dirname(__file__))

# Get the long description from the README file
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
long_description = f.read()

setup(
name='snakelet',

# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='0.2.0',

description='Mongo ORM',
long_description=long_description,

# The project's main homepage.
url='https://github.com/alexgurrola/snakelet',

# Author details
author='Alex Gurrola',
author_email='[email protected]',

# Choose your license
license='MIT',

# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 3 - Alpha',

# Indicate who your project is intended for
'Intended Audience :: Developers',
'Topic :: Database :: Front-Ends',

# Pick your license as you wish (should match "license" above)
'License :: OSI Approved :: MIT License',

# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6'
],

# What does your project relate to?
keywords='data mongo orm',

# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages().
packages=find_packages(exclude=['contrib', 'docs', 'tests']),

# Alternatively, if you want to distribute just a my_module.py, uncomment
# this:
# py_modules=["my_module"],

# List run-time dependencies here. These will be installed by pip when
# your project is installed. For an analysis of "install_requires" vs pip's
# requirements files see:
# https://packaging.python.org/en/latest/requirements.html
install_requires=[
'plac',
'pymongo'
],

# List additional groups of dependencies here (e.g. development
# dependencies). You can install these using the following syntax,
# for example:
# $ pip install -e .[dev,test]
extras_require={
'dev': ['check-manifest'],
'test': ['coverage'],
},

# If there are data files included in your packages that need to be
# installed, specify them here. If using Python 2.6 or less, then these
# have to be included in MANIFEST.in as well.
# package_data={
# 'sample': ['package_data.dat'],
# },

# Although 'package_data' is the preferred approach, in some case you may
# need to place data files outside of your packages. See:
# http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
# data_files=[
# ('my_data', [
# 'data/dseeds_dataset.cv'
# ])
# ],

# To provide executable scripts, use entry points in preference to the
# "scripts" keyword. Entry points provide cross-platform support and allow
# pip to create the appropriate form of executable for the target platform.
# entry_points={
# 'console_scripts': [
# 'sample=sample:main',
# ],
# },
)
8 changes: 8 additions & 0 deletions snakelet/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Snakelet is a Mongo ORM using Native Drivers without Schemas
"""

__version__ = '0.2.0'
__author__ = 'Alex Gurrola'
__email__ = '[email protected]'
__url__ = 'https://github.com/alexgurrola/snakelet'
13 changes: 13 additions & 0 deletions snakelet/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
def main(version: ("display version", 'flag', 'a')):
import snakelet
if version:
print(snakelet.__version__)


if __name__ == '__main__':
import plac

try:
plac.call(main)
except KeyboardInterrupt:
print('\nGoodbye!')
5 changes: 5 additions & 0 deletions snakelet/storage/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .document import Document

__all__ = (
'Document'
)
27 changes: 27 additions & 0 deletions snakelet/storage/annotation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from .document import Document


def foo(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)

return inner


class Cat(Document):
def __init__(self, name: str, **kwargs):
# Parental
super().__init__(**kwargs)
self.name = name

@foo
def meow(self):
return self.name


litter = [
Cat('kitty'),
Cat('cutey')
]

print('meow:', [cat.meow() for cat in litter])
50 changes: 50 additions & 0 deletions snakelet/storage/collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from .paginator import Paginator
from ..utilities.conversion import snake


class Collection:
def __init__(self, manager, document):
self.manager = manager
self.document = document
self.collection_name = snake(self.document.__name__)
self.collection = self.manager.db[self.collection_name]

def find(self, search):
return self.manager.find(self.collection_name, search)

def find_one(self, search):
return self.manager.find_one(self.collection_name, search)

def save(self, document):
if '_id' not in document:
self.collection.insert(document)
else:
self.collection.update({"_id": document['_id']}, document)

def refresh(self, document):
if '_id' in document:
document.update(self.collection.find_one(document['_id']))

def remove(self, document):
if '_id' in document:
self.collection.remove({"_id": document['_id']})
document.clear()

def paginate(self, **kwargs):
return Paginator(self, **kwargs)

def objectify(self, document):
return self.manager.objectify(self.collection_name, document)


def main():
pass


if __name__ == '__main__':
import plac

try:
plac.call(main)
except KeyboardInterrupt:
print('\nGoodbye!')
28 changes: 28 additions & 0 deletions snakelet/storage/document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Document(dict):
__getattr__ = dict.get
__delattr__ = dict.__delitem__
__setattr__ = dict.__setitem__

"""
def __init__(self, **kwargs):
# Parental
super().__init__(**kwargs)
self.__meta__ = {}
def __add_meta__(self, key, type):
self.__meta__[key] = type
return
"""


def main():
pass


if __name__ == '__main__':
import plac

try:
plac.call(main)
except KeyboardInterrupt:
print('\nGoodbye!')
84 changes: 84 additions & 0 deletions snakelet/storage/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import configparser

from bson.dbref import DBRef
from pymongo import MongoClient

from .collection import Collection
from ..utilities.conversion import camel


class Manager(dict):
__getattr__ = dict.get
__delattr__ = dict.__delitem__
__setattr__ = dict.__setitem__

config = configparser.ConfigParser()
config.read('config.ini')
config = config['storage']
client = MongoClient(config['host'], config.getint('port'))
client[config['db']].authenticate(config['user'], config.get('pass', raw=True))
db = client[config['db']]
documents = {}
ref_types = (dict, list, DBRef)

def register(self, document):
identifier = document.__name__
if identifier not in self.documents:
self.documents[identifier] = document
self[identifier] = Collection(self, document)
return True
return False

def objectify(self, collection, document):
name = camel(collection)
if name in self.documents:
prototype = self.documents[name]()
if document:
prototype.update(document)
# TODO: There needs to be a 'proxy' object that holds the DBRef and hydrates
# self.hydrate(prototype)
return prototype
return document

def hydrate(self, target):
if isinstance(target, DBRef):
document = self.find_one(target.collection, target.id)
if document:
document = self.hydrate(self.objectify(target.collection, document))
return document
elif isinstance(target, dict):
for key, value in target.items():
if isinstance(value, self.ref_types):
target[key] = self.hydrate(value)
elif isinstance(target, list):
for i, value in enumerate(target):
if isinstance(value, self.ref_types):
target[i] = self.hydrate(value)

# return target if nothing else occurred
return target

def find(self, collection, search):
documents = []
for document in self.db[collection].find(search):
documents.append(self.objectify(collection, document))
return documents

def find_one(self, collection, search):
return self.objectify(collection, self.db[collection].find_one(search))

def shutdown(self):
pass


def main():
pass


if __name__ == '__main__':
import plac

try:
plac.call(main)
except KeyboardInterrupt:
print('\nGoodbye!')
Loading

0 comments on commit 95b0b93

Please sign in to comment.