Package: bali-core
🏝 Simplify Cloud Native Microservices development base on FastAPI and gRPC.Documentation:https://bali-framework.github.io/bali/BaliBali is a framework integrate FastAPI and gRPC.
If you want to provide both HTTP and RPC, it can improve development efficiency.It gives you the following features:A simple layout of file structure rule.IntegratedSQLAlchemyORM and provide generic model methods.Utilities of transform models to Pydantic schemas.GZipMiddleware included and GZip decompression enabled.🍻Resourcelayer to write code once support both HTTP and RPCWho's using bali frameworkRequirements1. Python 3.7+
2. FastAPI 0.63+
3. grpcio>=1.32,<1.50Installpipinstallbali-core# Bali frameworkpipinstallbali-cli# Bali command line toolProject structure layoutApplicationCreate Applicationapp=Bali()# Initialized AppLaunch# With bali-clibalirunhttp
balirunrpc
balirunevent

pythonmain.pyrun--http# launch HTTP in development modepythonmain.pyrun--rpc# launch RPCpythonmain.pyrun--event# launch EventMore usage ofApplication:exampleDatabaseconnectfrombaliimportdb# connect to database when app started# db is a sqla-wrapper instancedb.connect('DATABASE_URI')Declarative mode with sqla-wrapperclassUser(db.Model):__tablename__"users"id=db.Column(db.Integer,primary_key=True)...db.create_all()db.add(User(...))db.commit()todos=db.query(User).all()More convenient usage, ref toSQLA-WrapperDeclare models inherit from convenient base modelsBaseModel# using BaseModelclassUser(db.BaseModel):__tablename__"users"id=db.Column(db.Integer,primary_key=True)...# BaseModel's source codeclassBaseModel(db.Model):__abstract__=Truecreated_time=Column(DateTime(timezone=True),default=datetime.utcnow)updated_time=Column(DateTime(timezone=True),default=datetime.utcnow,onupdate=datetime.utcnow)is_active=Column(Boolean(),default=True)TransactionSQLA-wrapper default model behavior is auto commit, auto commit will be disabled withdb.transactioncontext.withdb.transaction():item=Item.create(name='test1')OperatorsOperators providedget_filters_exprto transform filters (dict) to SQLAlchemy expressions.frombali.db.operatorsimportget_filters_exprfrommodelsimportUserusers=User.query().filter(*get_filters_expr(User,**filters)).all()Schemamodel_to_schema# generate pydantic schema from models# `User` is a db.Model or db.BaseModel instancefrombali.schemasimportmodel_to_schemaUserSchema=model_to_schema(User)ResourceNew in version 2.0.Resource’s design borrows several key concepts from the REST architectural style.Inspired byViewSetin Django REST Framework.Actions' name accordingStandard methodsin Google API design guideGeneric HTTP/RPC ActionsGeneric HTTP/RPC support actions:ActionRouteMethodRPCDescriptionget/{id}GETGet{Resource}Get an existing resource matching the given idlist/GETList{Resource}Get all the resourcescreate/POSTCreate{Resource}Create a new resourceupdate/{id}PATCHUpdate{Resource}Update an existing resource matching the given iddelete/{id}DELETEDelete{Resource}Delete an existing resource matching the given idGeneric Actions examples:# 1. import `Resource` base classfrombali.resourcesimportResource# 2. implementation actions inherited from ResourceclassGreeterResource(Resource):schema=Greeter@action()defget(self,pk=None):return[gforginGREETERSifg.get('id')==pk][0]@action()deflist(self,schema_in:ListRequest):returnGREETERS[:schema_in.limit]@action()defcreate(self,schema_in:schema):return{'id':schema_in.id,'content':schema_in.content}@action()defupdate(self,schema_in:schema,pk=None):return{'id':pk,'content':schema_in.content}@action()defdelete(self,pk=None):return{'id':pk,'result':True}# using `id` instand of `result`Custom HTTP/RPC ActionsCustom actions also decorated by@action, butdetailsignature is required.@action(detail=False)defcustom_action(self):passdetailhas no default value.Truemeans action to single resource, url path is '/{resources}/{id}'.Falsemeans action set of resources, url path is '/{resources}'.Override HTTP ActionsIf the default HTTP action template is not satisfied your request, you can override HTTP actions.# Get the origin routerrouter=GreeterResource.as_router()# Override the actions using the FastAPI normal way@router.get("/")defroot():return{"message":"Hello World"}More usage ofResource:GreeterResourceModelResourceNew in version 2.1.classUserResource(ModelResource):model=Userschema=UserSchemafilters=[{'username':str},{'age':Optional[str]},]# yapf: disablepermission_classes=[IsAuthenticated]Service Mixin# importfrombali.mixinsimportServiceMixinclassHello(hello_pb2_grpc.HelloServiceServicer,ServiceMixin):passCacheCache APIfrombaliimportcache# Usage example (API)# Read cachecache.get(key)# Set cachecache.set(key,value,timeout=10)cache memoize# Import the cache_memoize from bali corefrombaliimportcache_memoize# Attach decorator to cacheable function with a timeout of 100 seconds.@cache_memoize(100)defexpensive_function(start,end):returnrandom.randint(start,end)Utilsdateparserdateparser docsMessageToDict/ParseDictOptimized MessageToDict/ParseDict fromgoogle.protobuf.js_formatfrombali.utilsimportMessageToDict,ParseDictTestsgRPC service testsfrombali.testsimportGRPCTestBasefromservice.demoimportdemo_service,demo_pb2,demo_pb2_grpcclassTestDemoRPC(GRPCTestBase):server_class=demo_service.DemoService# Provided servicepb2=demo_pb2# Provided pb2pb2_grpc=demo_pb2_grpc# Provided pb2 grpcdefsetup_method(self):# Pytest setuppassdefteardown_method(self):# Pytest teardownpassdeftest_demo(self):passRelated Projects