Skip to content

Commit

Permalink
begin reworking query subsystem
Browse files Browse the repository at this point in the history
  • Loading branch information
renanthera committed Jan 15, 2024
1 parent fee9a31 commit 673ec7f
Show file tree
Hide file tree
Showing 9 changed files with 12,546 additions and 58 deletions.
178 changes: 168 additions & 10 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,16 @@
# 'XDk2aVCLnyBFKHPr',
# ] )

analyzers.flaming_germination.flaming_germination(
[
'AVRh3LdawCzY8HKB'
],
[['Lagrimas', 'Katebrew', 'Dandaniel', 'Chizgoblin'],
['Draxxii', 'Diosita', 'Goines', 'Ralfie'],
['Meowge', 'Softmograne', 'Zinglefus', 'Jeefy'],
['Impdh', 'Skovrogue', 'Reddevviil', 'Yusoon'],
['Stragnim', 'Kreemlock', 'Zenvi', 'Harreks']]
)
# analyzers.flaming_germination.flaming_germination(
# [
# 'AVRh3LdawCzY8HKB'
# ],
# [['Lagrimas', 'Katebrew', 'Dandaniel', 'Chizgoblin'],
# ['Draxxii', 'Diosita', 'Goines', 'Ralfie'],
# ['Meowge', 'Softmograne', 'Zinglefus', 'Jeefy'],
# ['Impdh', 'Skovrogue', 'Reddevviil', 'Yusoon'],
# ['Stragnim', 'Kreemlock', 'Zenvi', 'Harreks']]
# )

# analyzers.flaming_germination.flaming_germination(
# [
Expand All @@ -151,3 +151,161 @@
# ['Learning', 'Eyebrowz', 'Glimmerer', 'Tacofajita', 'Hunkytwunky', 'Bobblywobbly']
# ]
# )

import wcl

# t = wcl.GQL_Object( {'asdf': 'fdsa'}, {'jkl': 'lkj'} )
# print(t)

# class Test:
# asdf = 'jkl'

# d = {
# '1': Test
# }

# for v in d.values():
# print(isinstance(Test(), v))

# u = wcl.GQL_Test1( {}, {} ).tree()
# v = wcl.GQL_Test2( {}, {} ).tree()
# print(u)
# print(v)
class GQL_OBJECT:
active = []
name = 'GQL_OBJECT'
def __init__( self, child_class_name = '', child_str = '' ):
self.child_class_name = child_class_name
self.child_str = child_str

self.parent = self.__class__.__mro__[1]
self.children = list(self.__class__.__subclasses__())

def __str__( self ):
children = [
self.child_str if child.name == self.child_class_name else str( child.name )
for child in self.children
if child.name in self.active
]
ret = self.name
if len( children ):
ret += '{' + ', '.join( children ) + '}'
if self.parent.__name__ != 'GQL_OBJECT':
return str( self.parent( self.name, ret ) )
return '{' + ret + '}'

class query( GQL_OBJECT ):
name = 'query'
active = [ 'report' ]
report = lambda *args, **kwargs: eval( '_QrsNKiY_WLpJ6Iu_report' )( *args, **kwargs )

class _QrsNKiY_WLpJ6Iu_report( query, GQL_OBJECT ):
name = 'report'
active = [ 'code', 'events' ]
code = lambda *args, **kwargs: eval( '_MqAkvZzWS6CGIGj_code' )( *args, **kwargs )
events = lambda *args, **kwargs: eval( '_GZ3efcSVmPvraRn_events' )( *args, **kwargs )

class _MqAkvZzWS6CGIGj_code( _QrsNKiY_WLpJ6Iu_report, GQL_OBJECT ):
name = 'code'

class _GZ3efcSVmPvraRn_events( _QrsNKiY_WLpJ6Iu_report, GQL_OBJECT ):
name = 'events'

print( query().report().events() )

import unicodedata
from keyword import iskeyword
import random
import sys

def is_allowed_unicode_char( c, position ):
# Other_ID_Start and Other_ID_Continue are from the following webpage with the matching category (?) name
# https://www.unicode.org/Public/15.0.0/ucd/PropList.txt
Other_ID_Start = { '\u1885', '\u1886', '\u2118', '\u212E', '\u309B', '\u309C' }
Other_ID_Continue = { '\u00B7', '\u0387', '\u1369', '\u1370', '\u1371', '\u19DA' }
# id_start and id_continue category_codes are defined from the following webpage:
# https://docs.python.org/3/reference/lexical_analysis.html#identifiers
id_start_category_codes = { 'Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl' }
id_continue_category_codes = id_start_category_codes | { 'Mn', 'Mc', 'Nd', 'Pc' }
# id_start and id_continue singletons are defined from the following webpage:
# https://docs.python.org/3/reference/lexical_analysis.html#identifiers
id_start_singletons = { '_' } | Other_ID_Start
id_continue_singletons = id_start_singletons | Other_ID_Continue

if unicodedata.category( c ) in id_start_category_codes or c in id_start_singletons:
return c
if position > 0 and ( unicodedata.category( c ) in id_continue_category_codes or c in id_continue_singletons ):
return c

def generate_unicode_identifier( length = 16 ):
# This implementation is correct, but returned strings are:
# - Unmanageably unreadable
# - Not reliably generated due to too small of a character set (to improve readability)
# - Requires too much pregeneration (to avoid unreliable generation)

# This does not generate all possible unicode identifiers, as I did not find
# a way to find all characters that NFKC normalize to a valid character.
def generate_chr(attempt = 0):
c = chr( random.randrange( sys.maxunicode + 1 ) )
if is_allowed_unicode_char( c, 0 ):
return c
return generate_chr( attempt + 1 )

candidate_str = ''.join( [
c
for _ in range( length )
if ( c := generate_chr() )
] )

if not iskeyword( candidate_str ):
return candidate_str
return generate_unicode_identifier( length = length )

def generate_ascii_identifier( length = 16 ):
# Only a small subset of valid Python 3 Identifiers, but:
# - Highly readable
# - Still millions of combinations
# - More trivial to generate
MIN_ASCII = 33
MAX_ASCII = 126

def generate_chr( position, attempts = 0 ):
character = chr( random.randrange( MIN_ASCII, MAX_ASCII ) )
if is_allowed_unicode_char( character, position ):
return character
return generate_chr( position, attempts + 1)

candidate_str = '_' + ''.join( [
generate_chr( pos )
for pos in range( length - 1 )
] )

if not iskeyword( candidate_str ) and candidate_str.isidentifier():
return candidate_str
return generate_ascii_identifier( length = length )


# for k in range( 5 ):
# ident = generate_ascii_identifier()
# print(ident, len(ident))



# PROBLEM:
# - nested classes are gross
# - having no nested classes with their actual names results in namespace clashes
# SOLUTION:
# a)
# - queries with no parents get their actual name for their object name
# - queries with parents get an internal name, and are added to the parent with their real name via `__subclasses__()` and `setattr(...)`
# b)
# - infrastructure exists for type hoisting, but it may not be useful in this situation

# print(C())
# print(C().D())

# print( A() )
# for q in A().children:
# print( q(), q().parent() )
# for u in q().children:
# print( u(), u().parent() )
12 changes: 6 additions & 6 deletions wcl/caching.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import json
import os
import uuid
import json, os, uuid

# Caches queries by identifier in a lookup table in `repo root/cache`.
# As implemented in requests, identifier is completed query that has been
Expand All @@ -11,12 +9,14 @@ def decorator( query ):
ret = None
cache = Cache()

query_str = str( query )

if query.cacheable:
ret = Request( query, cache.get_artifact( query.string ) )
ret = Request( query, cache.get_artifact( query_str ) )
if ret is None:
ret = Request( query )
if query.cacheable and not cache.lookup_uuid( query.string ):
cache.put_artifact( query.string, ret.data )
if query.cacheable and not cache.lookup_uuid( query_str ):
cache.put_artifact( query_str, ret.data )
return ret

return decorator
Expand Down
13 changes: 6 additions & 7 deletions wcl/interface.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import json

from . import query
from .requests import Request
from . import query, request

def getFights( params ):
return Request( query.Fights( params ) ).data
return request.Request( query.Fights( params ) ).data

def getPlayerDetails( params ):
return Request( query.PlayerDetails( params )
return request.Request( query.PlayerDetails( params )
).data.get( 'data' ).get( 'playerDetails' ) # pyright: ignore

def getMasterData( params ):
return Request( query.Actors( params ) ).data
return request.Request( query.Actors( params ) ).data

def getEvents( params ):
return Request( query.Events( params ) ).data.get( 'data' ) or {} # pyright: ignore
return request.Request( query.Events( params ) ).data.get( 'data' ) or {} # pyright: ignore

def printFights( params ):
req = getFights( params )
Expand Down Expand Up @@ -49,7 +48,7 @@ def printPlayerDetails( params ):

def getPointsSpent():
params = {}
req = Request( query.PointsSpentThisHour( params ) ).data.get( 'pointsSpentThisHour' )
req = request.Request( query.PointsSpentThisHour( params ) ).data.get( 'pointsSpentThisHour' )
return print( req, 'point' if req == 1 else 'points', 'spent this hour' ) # pyright: ignore

def getPlayerFromID( id, params ):
Expand Down
99 changes: 99 additions & 0 deletions wcl/introspection_query.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}

fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}

fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}

fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
4 changes: 1 addition & 3 deletions wcl/token.py → wcl/oauth_token.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import time
import json
import pickle
import time, json, pickle

from requests.exceptions import HTTPError
from oauthlib.oauth2 import BackendApplicationClient
Expand Down
Loading

0 comments on commit 673ec7f

Please sign in to comment.