Version 269

This commit is contained in:
Hydrus Network Developer 2017-08-16 16:58:06 -05:00
parent d55477e373
commit 9b22a0ac22
26 changed files with 2244 additions and 1365 deletions

View File

@ -8,6 +8,34 @@
<div class="content">
<h3>changelog</h3>
<ul>
<ul>
<li><h3>version 269</h3></li>
<li>nested pages now supported!</li>
<li>moved all page management (session load/save, new/close page, page navigation, page name maintenance, etc...) code from the main gui to the new PagesNotebook object</li>
<li>expanded the session object to hold nested page information</li>
<li>added a 'page of pages' page to the 'special' new page entry!</li>
<li>numerous other gui-notebook page-related event fixes and improvements</li>
<li>figured out cross-platform menu and other mouse event support for nested notebooks, but there may still be holes--please let me know if your new pages ever appear in the wrong tier!</li>
<li>ways to move pages up and down rows will come in the coming weeks!</li>
<li>main gui status bar now shows total bandwidth this session as well as current speed</li>
<li>added a cog button to network job controls that allow for manual- and auto-bandwidth-override</li>
<li>added a 'blocked?' column to the review bandwidth panel to quickly see which network contexts are currently available to do work</li>
<li>added a button to reset 'default' and 'global' bandwidth rules to review bandwidth panel</li>
<li>merged the network request start test and consumption code into one transaction, stopping some accidental overconsumption when the engine was under heavy load</li>
<li>did some logic work to make sure unusual network context rule/usage situations are visible on the review bandwidth panel for editing</li>
<li>network jobs now report more information as they get ready to start, including while they are held up waiting for a download slot</li>
<li>added a simple 'network profile mode' to the debug menu that atm just prints a summary of new jobs</li>
<li>fixed an error that could sometimes be a crash when 'review services' was opened while no pages were open in the main gui</li>
<li>fixed the pixiv login test button with a hacky workaround--I will make extensive proper login testing gui when I move to the login engine</li>
<li>fixed an issue in the youtube downloader</li>
<li>if the bandwidth or session managers are missing on boot, empty defaults will be created in their stead</li>
<li>bumped the max page limit up to 150--I expect to increase it more in the coming months as I rejigger how some gui stuff is laid out</li>
<li>renamed 'sort by age' to 'time imported'</li>
<li>fixed and improved some test code</li>
<li>cleaned up some shutdown code</li>
<li>should have fixed a rare unicode conversion issue when printing to log</li>
<li>misc improvements</li>
</ul>
<li><h3>version 268</h3></li>
<ul>
<li>split the sort dropdown into two, splitting the sort type and sort order</li>

View File

@ -3362,7 +3362,7 @@ class WebSessionManagerClient( object ):
if 'pixivAccount.postKey' not in j:
raise HydrusExceptions.ForbiddenException( 'When trying to log into Pixiv, I could not find the POST key!' )
raise HydrusExceptions.ForbiddenException( 'When trying to log into Pixiv, I could not find the POST key! This is a problem with hydrus\'s pixiv parsing, not your login! Please contact hydrus dev!' )
post_key = j[ 'pixivAccount.postKey' ]
@ -3386,3 +3386,80 @@ class WebSessionManagerClient( object ):
r = session.post( 'https://accounts.pixiv.net/api/login?lang=en', data = form_fields, headers = headers )
def TestPixiv( self, pixiv_id, password ):
# this is just an ugly copy, but fuck it for the minute
# we'll figure out a proper testing engine later with the login engine and tie the manage gui into it as well
session = requests.Session()
response = session.get( 'https://accounts.pixiv.net/login' )
soup = ClientDownloading.GetSoup( response.content )
# some whocking 20kb bit of json tucked inside a hidden form input wew lad
i = soup.find( 'input', id = 'init-config' )
raw_json = i['value']
j = json.loads( raw_json )
if 'pixivAccount.postKey' not in j:
return ( False, 'When trying to log into Pixiv, I could not find the POST key! This is a problem with hydrus\'s pixiv parsing, not your login! Please contact hydrus dev!' )
post_key = j[ 'pixivAccount.postKey' ]
form_fields = {}
form_fields[ 'pixiv_id' ] = pixiv_id
form_fields[ 'password' ] = password
form_fields[ 'captcha' ] = ''
form_fields[ 'g_recaptcha_response' ] = ''
form_fields[ 'return_to' ] = 'https://www.pixiv.net'
form_fields[ 'lang' ] = 'en'
form_fields[ 'post_key' ] = post_key
form_fields[ 'source' ] = 'pc'
headers = {}
headers[ 'referer' ] = "https://accounts.pixiv.net/login?lang=en^source=pc&view_type=page&ref=wwwtop_accounts_index"
headers[ 'origin' ] = "https://accounts.pixiv.net"
r = session.post( 'https://accounts.pixiv.net/api/login?lang=en', data = form_fields, headers = headers )
if not r.ok:
HydrusData.ShowText( r.content )
return ( False, 'Login request failed! Info printed to log.' )
cookies = session.cookies
cookies.clear_expired_cookies()
domains = cookies.list_domains()
for domain in domains:
if domain.endswith( 'pixiv.net' ):
d = cookies.get_dict( domain )
if 'PHPSESSID' not in d:
HydrusData.ShowText( r.content )
return ( False, 'Pixiv login failed to establish session! Info printed to log.' )
return ( True, '' )
HydrusData.ShowText( r.content )
return ( False, 'Pixiv login failed to establish session! Info printed to log.' )

View File

@ -29,7 +29,7 @@ COLOUR_UNSELECTED = wx.Colour( 223, 227, 230 )
COLOUR_MESSAGE = wx.Colour( 230, 246, 255 )
SHORTCUT_HELP = '''You can set up many custom shortcuts in file->options->shortcuts. Please check that to see your current mapping.
SHORTCUT_HELP = '''You can set up many custom shortcuts in file->shortcuts. Please check that to see your current mapping.
Some shortcuts remain hardcoded, however:

View File

@ -577,7 +577,25 @@ class Controller( HydrusController.HydrusController ):
#
bandwidth_manager = self.Read( 'serialisable', HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER )
if bandwidth_manager is None:
bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
wx.MessageBox( 'Your bandwidth manager was missing on boot! I have recreated a new empty one with default rules. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery.' )
session_manager = self.Read( 'serialisable', HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER )
if session_manager is None:
session_manager = ClientNetworking.NetworkSessionManager()
wx.MessageBox( 'Your session manager was missing on boot! I have recreated a new empty one. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery.' )
login_manager = ClientNetworking.NetworkLoginManager()
self.network_engine = ClientNetworking.NetworkEngine( self, bandwidth_manager, session_manager, login_manager )

View File

@ -2846,7 +2846,9 @@ class DB( HydrusDB.HydrusDB ):
self._SetJSONDump( shortcuts )
bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
self._SetJSONDump( bandwidth_manager )
@ -4896,13 +4898,21 @@ class DB( HydrusDB.HydrusDB ):
def _GetJSONDump( self, dump_type ):
( version, dump ) = self._c.execute( 'SELECT version, dump FROM json_dumps WHERE dump_type = ?;', ( dump_type, ) ).fetchone()
result = self._c.execute( 'SELECT version, dump FROM json_dumps WHERE dump_type = ?;', ( dump_type, ) ).fetchone()
serialisable_info = json.loads( dump )
if result is None:
return result
else:
( version, dump ) = result
serialisable_info = json.loads( dump )
return HydrusSerialisable.CreateFromSerialisableTuple( ( dump_type, version, serialisable_info ) )
return HydrusSerialisable.CreateFromSerialisableTuple( ( dump_type, version, serialisable_info ) )
def _GetJSONDumpNamed( self, dump_type, dump_name = None ):
@ -9610,7 +9620,9 @@ class DB( HydrusDB.HydrusDB ):
#
bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
ClientDefaults.SetDefaultBandwidthManagerRules( bandwidth_manager )
self._SetJSONDump( bandwidth_manager )
@ -9683,7 +9695,9 @@ class DB( HydrusDB.HydrusDB ):
if version == 264:
default_bandwidth_manager = ClientDefaults.GetDefaultBandwidthManager()
default_bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
ClientDefaults.SetDefaultBandwidthManagerRules( default_bandwidth_manager )
bandwidth_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER )

View File

@ -7,14 +7,12 @@ import HydrusNetworking
import os
import wx
def GetDefaultBandwidthManager():
def SetDefaultBandwidthManagerRules( bandwidth_manager ):
KB = 1024
MB = 1024 ** 2
GB = 1024 ** 3
bandwidth_manager = ClientNetworking.NetworkBandwidthManager()
#
rules = HydrusNetworking.BandwidthRules()
@ -82,10 +80,6 @@ def GetDefaultBandwidthManager():
bandwidth_manager.SetRules( ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_THREAD_WATCHER_THREAD ), rules )
#
return bandwidth_manager
def GetClientDefaultOptions():
options = {}

File diff suppressed because it is too large Load Diff

View File

@ -1435,7 +1435,10 @@ class MenuBitmapButton( BetterBitmapButton ):
current_value = check_manager.GetCurrentValue()
func = check_manager.Invert
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
if current_value is not None:
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
elif item_type == 'separator':
@ -1475,6 +1478,7 @@ class MenuButton( BetterButton ):
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, check_manager.Invert )
elif item_type == 'separator':
ClientGUIMenus.AppendSeparator( menu )

View File

@ -350,6 +350,8 @@ class NetworkJobControl( wx.Panel ):
self._network_job = None
self._download_started = False
self._auto_override_bandwidth_rules = False
self._left_text = ClientGUICommon.BetterStaticText( self )
self._right_text = ClientGUICommon.BetterStaticText( self, style = wx.ALIGN_RIGHT )
@ -360,6 +362,25 @@ class NetworkJobControl( wx.Panel ):
self._gauge = ClientGUICommon.Gauge( self )
menu_items = []
invert_call = self.FlipOverrideBandwidthForCurrentJob
value_call = self.CurrentJobOverridesBandwidth
check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
menu_items.append( ( 'check', 'override bandwidth rules for this job', 'Tell the current job to ignore existing bandwidth rules and go ahead anyway.', check_manager ) )
menu_items.append( ( 'separator', 0, 0, 0 ) )
invert_call = self.FlipAutoOverrideBandwidth
value_call = self.AutoOverrideBandwidth
check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
menu_items.append( ( 'check', 'auto-override bandwidth rules for all jobs here after five seconds', 'Ignore existing bandwidth rules for all jobs under this control, instead waiting a flat five seconds.', check_manager ) )
self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
self._cancel_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.stop, self.Cancel )
#
@ -381,6 +402,7 @@ class NetworkJobControl( wx.Panel ):
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( left_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
hbox.AddF( self._cog_button, CC.FLAGS_VCENTER )
hbox.AddF( self._cancel_button, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
@ -405,6 +427,11 @@ class NetworkJobControl( wx.Panel ):
else:
if self._auto_override_bandwidth_rules and HydrusData.TimeHasPassed( self._network_job.GetCreationTime() + 5 ):
self._network_job.OverrideBandwidth()
if self._network_job.IsDone():
can_cancel = False
@ -471,6 +498,11 @@ class NetworkJobControl( wx.Panel ):
def AutoOverrideBandwidth( self ):
return self._auto_override_bandwidth_rules
def Cancel( self ):
if self._network_job is not None:
@ -491,6 +523,31 @@ class NetworkJobControl( wx.Panel ):
def CurrentJobOverridesBandwidth( self ):
if self._network_job is None:
return None
else:
return not self._network_job.ObeysBandwidth()
def FlipAutoOverrideBandwidth( self ):
self._auto_override_bandwidth_rules = not self._auto_override_bandwidth_rules
def FlipOverrideBandwidthForCurrentJob( self ):
if self._network_job is not None:
self._network_job.OverrideBandwidth()
def SetNetworkJob( self, network_job ):
if self and self._network_job != network_job:

View File

@ -1811,313 +1811,6 @@ class DialogModifyAccounts( Dialog ):
class DialogPageChooser( Dialog ):
def __init__( self, parent ):
Dialog.__init__( self, parent, 'new page', position = 'center' )
# spawn in this order, so focus precipitates from the graphical top
self._button_7 = wx.Button( self, label = '', id = 7 )
self._button_8 = wx.Button( self, label = '', id = 8 )
self._button_9 = wx.Button( self, label = '', id = 9 )
self._button_4 = wx.Button( self, label = '', id = 4 )
self._button_5 = wx.Button( self, label = '', id = 5 )
self._button_6 = wx.Button( self, label = '', id = 6 )
self._button_1 = wx.Button( self, label = '', id = 1 )
self._button_2 = wx.Button( self, label = '', id = 2 )
self._button_3 = wx.Button( self, label = '', id = 3 )
gridbox = wx.GridSizer( 0, 3 )
gridbox.AddF( self._button_7, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_8, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_9, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_4, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_5, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_6, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_1, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_2, CC.FLAGS_EXPAND_BOTH_WAYS )
gridbox.AddF( self._button_3, CC.FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( gridbox )
self.SetInitialSize( ( 420, 210 ) )
self._services = HG.client_controller.services_manager.GetServices()
repository_petition_permissions = [ ( content_type, HC.PERMISSION_ACTION_OVERRULE ) for content_type in HC.REPOSITORY_CONTENT_TYPES ]
self._petition_service_keys = [ service.GetServiceKey() for service in self._services if service.GetServiceType() in HC.REPOSITORIES and True in ( service.HasPermission( content_type, action ) for ( content_type, action ) in repository_petition_permissions ) ]
self._InitButtons( 'home' )
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self.Bind( wx.EVT_BUTTON, self.EventButton )
#
self.Show( True )
def _AddEntry( self, button, entry ):
id = button.GetId()
self._command_dict[ id ] = entry
( entry_type, obj ) = entry
if entry_type == 'menu':
button.SetLabelText( obj )
elif entry_type == 'page_duplicate_filter':
button.SetLabelText( 'duplicates processing' )
elif entry_type in ( 'page_query', 'page_petitions' ):
name = HG.client_controller.services_manager.GetService( obj ).GetName()
button.SetLabelText( name )
elif entry_type == 'page_import_booru':
button.SetLabelText( 'booru' )
elif entry_type == 'page_import_gallery':
site_type = obj
text = HC.site_type_string_lookup[ site_type ]
button.SetLabelText( text )
elif entry_type == 'page_import_page_of_images':
button.SetLabelText( 'page of images' )
elif entry_type == 'page_import_thread_watcher':
button.SetLabelText( 'thread watcher' )
elif entry_type == 'page_import_urls':
button.SetLabelText( 'raw urls' )
button.Show()
def _HitButton( self, id ):
if id in self._command_dict:
( entry_type, obj ) = self._command_dict[ id ]
if entry_type == 'menu':
self._InitButtons( obj )
else:
if entry_type == 'page_query':
HG.client_controller.pub( 'new_page_query', obj )
elif entry_type == 'page_duplicate_filter':
HG.client_controller.pub( 'new_duplicate_filter' )
elif entry_type == 'page_import_booru':
HG.client_controller.pub( 'new_import_booru' )
elif entry_type == 'page_import_gallery':
site_type = obj
HG.client_controller.pub( 'new_import_gallery', site_type )
elif entry_type == 'page_import_page_of_images':
HG.client_controller.pub( 'new_page_import_page_of_images' )
elif entry_type == 'page_import_thread_watcher':
HG.client_controller.pub( 'new_page_import_thread_watcher' )
elif entry_type == 'page_import_urls':
HG.client_controller.pub( 'new_page_import_urls' )
elif entry_type == 'page_petitions':
HG.client_controller.pub( 'new_page_petitions', obj )
self.EndModal( wx.ID_OK )
def _InitButtons( self, menu_keyword ):
self._command_dict = {}
entries = []
if menu_keyword == 'home':
entries.append( ( 'menu', 'files' ) )
entries.append( ( 'menu', 'download' ) )
if len( self._petition_service_keys ) > 0:
entries.append( ( 'menu', 'petitions' ) )
entries.append( ( 'menu', 'special' ) )
elif menu_keyword == 'files':
file_repos = [ ( 'page_query', service_key ) for service_key in [ service.GetServiceKey() for service in self._services if service.GetServiceType() == HC.FILE_REPOSITORY ] ]
entries.append( ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ) )
entries.append( ( 'page_query', CC.TRASH_SERVICE_KEY ) )
entries.append( ( 'page_query', CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
for service in self._services:
if service.GetServiceType() == HC.FILE_REPOSITORY:
entries.append( ( 'page_query', service.GetServiceKey() ) )
elif menu_keyword == 'download':
entries.append( ( 'page_import_urls', None ) )
entries.append( ( 'page_import_thread_watcher', None ) )
entries.append( ( 'menu', 'gallery' ) )
entries.append( ( 'page_import_page_of_images', None ) )
elif menu_keyword == 'gallery':
entries.append( ( 'page_import_booru', None ) )
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_DEVIANT_ART ) )
entries.append( ( 'menu', 'hentai foundry' ) )
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_NEWGROUNDS ) )
result = HG.client_controller.Read( 'serialisable_simple', 'pixiv_account' )
if result is not None:
entries.append( ( 'menu', 'pixiv' ) )
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_TUMBLR ) )
elif menu_keyword == 'hentai foundry':
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST ) )
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) )
elif menu_keyword == 'pixiv':
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_ARTIST_ID ) )
entries.append( ( 'page_import_gallery', HC.SITE_TYPE_PIXIV_TAG ) )
elif menu_keyword == 'petitions':
entries = [ ( 'page_petitions', service_key ) for service_key in self._petition_service_keys ]
elif menu_keyword == 'special':
entries.append( ( 'page_duplicate_filter', None ) )
if len( entries ) <= 4:
self._button_1.Hide()
self._button_3.Hide()
self._button_5.Hide()
self._button_7.Hide()
self._button_9.Hide()
potential_buttons = [ self._button_8, self._button_4, self._button_6, self._button_2 ]
elif len( entries ) <= 9:
potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
else:
# sort out a multi-page solution? maybe only if this becomes a big thing; the person can always select from the menus, yeah?
potential_buttons = [ self._button_7, self._button_8, self._button_9, self._button_4, self._button_5, self._button_6, self._button_1, self._button_2, self._button_3 ]
entries = entries[:9]
for entry in entries: self._AddEntry( potential_buttons.pop( 0 ), entry )
unused_buttons = potential_buttons
for button in unused_buttons: button.Hide()
def EventButton( self, event ):
id = event.GetId()
if id == wx.ID_CANCEL:
self.EndModal( wx.ID_CANCEL )
else:
self._HitButton( id )
def EventCharHook( self, event ):
id = None
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
if key == wx.WXK_UP: id = 8
elif key == wx.WXK_LEFT: id = 4
elif key == wx.WXK_RIGHT: id = 6
elif key == wx.WXK_DOWN: id = 2
elif key == wx.WXK_NUMPAD1: id = 1
elif key == wx.WXK_NUMPAD2: id = 2
elif key == wx.WXK_NUMPAD3: id = 3
elif key == wx.WXK_NUMPAD4: id = 4
elif key == wx.WXK_NUMPAD5: id = 5
elif key == wx.WXK_NUMPAD6: id = 6
elif key == wx.WXK_NUMPAD7: id = 7
elif key == wx.WXK_NUMPAD8: id = 8
elif key == wx.WXK_NUMPAD9: id = 9
elif key == wx.WXK_ESCAPE:
self.EndModal( wx.ID_CANCEL )
return
else:
event.Skip()
if id is not None:
self._HitButton( id )
class DialogPathsToTags( Dialog ):
def __init__( self, parent, paths ):
@ -3352,9 +3045,11 @@ class DialogSelectYoutubeURL( Dialog ):
#
self._info.sort()
keys = list( self._info.keys() )
for ( extension, resolution ) in self._info:
keys.sort()
for ( extension, resolution ) in keys:
self._urls.Append( ( extension, resolution ), ( extension, resolution ) )

View File

@ -2889,11 +2889,18 @@ class DialogManagePixivAccount( ClientGUIDialogs.Dialog ):
manager = HG.client_controller.GetManager( 'web_sessions' )
cookies = manager.GetPixivCookies( pixiv_id, password )
( result, message ) = manager.TestPixiv( pixiv_id, password )
self._status.SetLabelText( 'OK!' )
wx.CallLater( 5000, self._status.SetLabel, '' )
if result:
self._status.SetLabelText( 'OK!' )
wx.CallLater( 5000, self._status.SetLabel, '' )
else:
self._status.SetLabelText( message )
except HydrusExceptions.ForbiddenException as e:

View File

@ -13,6 +13,7 @@ import ClientGUIScrolledPanelsManagement
import ClientGUIShortcuts
import ClientGUITopLevelWindows
import ClientMedia
import ClientSearch
import ClientTags
import collections
import HydrusExceptions
@ -694,7 +695,9 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
hash = self._focussed_media.GetDisplayMedia().GetHash()
HG.client_controller.pub( 'new_similar_to', CC.LOCAL_FILE_SERVICE_KEY, hash, max_hamming )
initial_predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, ( hash, max_hamming ) ) ]
HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = initial_predicates )

File diff suppressed because it is too large Load Diff

View File

@ -450,7 +450,7 @@ class ReviewServicePanel( wx.Panel ):
def __init__( self, parent, service ):
ClientGUICommon.StaticBox.__init__( self, parent, 'clientside network' )
ClientGUICommon.StaticBox.__init__( self, parent, 'this client\'s network use' )
self._service = service
@ -530,7 +530,7 @@ class ReviewServicePanel( wx.Panel ):
def __init__( self, parent, service ):
ClientGUICommon.StaticBox.__init__( self, parent, 'serverside hydrus account' )
ClientGUICommon.StaticBox.__init__( self, parent, 'hydrus service account' )
self._service = service

View File

@ -615,7 +615,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
def __init__( self, parent, service_type, dictionary ):
ClientGUICommon.StaticBox.__init__( self, parent, 'clientside network' )
ClientGUICommon.StaticBox.__init__( self, parent, 'network connection' )
self._service_type = service_type

View File

@ -1,5 +1,6 @@
import ClientConstants as CC
import ClientData
import ClientDefaults
import ClientGUICommon
import ClientGUIDialogs
import ClientGUIFrames
@ -313,10 +314,12 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._history_time_delta_none = wx.CheckBox( self, label = 'show all' )
self._history_time_delta_none.Bind( wx.EVT_CHECKBOX, self.EventTimeDeltaChanged )
self._bandwidths = ClientGUIListCtrl.BetterListCtrl( self, 'bandwidth review', 20, 30, [ ( 'name', -1 ), ( 'type', 14 ), ( 'current usage', 14 ), ( 'past 24 hours', 14 ), ( 'this month', 14 ), ( 'has specific rules', 20 ) ], self._ConvertNetworkContextsToListCtrlTuples, activation_callback = self.ShowNetworkContext )
self._bandwidths = ClientGUIListCtrl.BetterListCtrl( self, 'bandwidth review', 20, 30, [ ( 'name', -1 ), ( 'type', 14 ), ( 'current usage', 14 ), ( 'past 24 hours', 15 ), ( 'this month', 12 ), ( 'has specific rules', 18 ), ( 'blocked?', 10 ) ], self._ConvertNetworkContextsToListCtrlTuples, activation_callback = self.ShowNetworkContext )
self._edit_default_bandwidth_rules_button = ClientGUICommon.BetterButton( self, 'edit default bandwidth rules', self._EditDefaultBandwidthRules )
self._reset_default_bandwidth_rules_button = ClientGUICommon.BetterButton( self, 'reset default bandwidth rules', self._ResetDefaultBandwidthRules )
default_rules_help_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.help, self._ShowDefaultRulesHelp )
default_rules_help_button.SetToolTipString( 'Show help regarding default bandwidth rules.' )
@ -347,6 +350,7 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._edit_default_bandwidth_rules_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._reset_default_bandwidth_rules_button, CC.FLAGS_VCENTER )
button_hbox.AddF( default_rules_help_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._delete_record_button, CC.FLAGS_VCENTER )
@ -393,7 +397,18 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
pretty_has_rules = ''
return ( ( pretty_network_context, pretty_context_type, pretty_current_usage, pretty_day_usage, pretty_month_usage, pretty_has_rules ), ( sortable_network_context, sortable_context_type, current_usage, day_usage, month_usage, has_rules ) )
blocked = not self._controller.network_engine.bandwidth_manager.CanDoWork( [ network_context ] )
if blocked:
pretty_blocked = 'yes'
else:
pretty_blocked = ''
return ( ( pretty_network_context, pretty_context_type, pretty_current_usage, pretty_day_usage, pretty_month_usage, pretty_has_rules, pretty_blocked ), ( sortable_network_context, sortable_context_type, current_usage, day_usage, month_usage, has_rules, blocked ) )
def _DeleteNetworkContexts( self ):
@ -442,6 +457,19 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
def _ResetDefaultBandwidthRules( self ):
message = 'Reset your \'default\' and \'global\' bandwidth rules to default?'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
ClientDefaults.SetDefaultBandwidthManagerRules( self._controller.network_engine.bandwidth_manager )
def _ShowDefaultRulesHelp( self ):
help = 'Network requests act in multiple contexts. Most use the \'global\' and \'web domain\' network contexts, but a hydrus server request, for instance, will add its own service-specific context, and a subscription will add both itself and its downloader.'

View File

@ -2105,7 +2105,7 @@ class MediaSort( HydrusSerialisable.SerialisableBase ):
sort_string_lookup[ CC.SORT_FILES_BY_FILESIZE ] = 'filesize'
sort_string_lookup[ CC.SORT_FILES_BY_DURATION ] = 'duration'
sort_string_lookup[ CC.SORT_FILES_BY_IMPORT_TIME ] = 'age'
sort_string_lookup[ CC.SORT_FILES_BY_IMPORT_TIME ] = 'time imported'
sort_string_lookup[ CC.SORT_FILES_BY_MIME ] = 'mime'
sort_string_lookup[ CC.SORT_FILES_BY_RANDOM ] = 'random'
sort_string_lookup[ CC.SORT_FILES_BY_WIDTH ] = 'width'

View File

@ -1112,6 +1112,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
def _CanStartRequest( self, network_contexts ):
for network_context in network_contexts:
bandwidth_rules = self._GetRules( network_context )
bandwidth_tracker = self._network_contexts_to_bandwidth_trackers[ network_context ]
if not bandwidth_rules.CanStartRequest( bandwidth_tracker ):
return False
return True
def _GetRules( self, network_context ):
if network_context not in self._network_contexts_to_bandwidth_rules:
@ -1152,6 +1169,16 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
def _ReportRequestUsed( self, network_contexts ):
for network_context in network_contexts:
self._network_contexts_to_bandwidth_trackers[ network_context ].ReportRequestUsed()
self._SetDirty()
def _SetDirty( self ):
self._dirty = True
@ -1177,7 +1204,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
def CanDoWork( self, network_contexts, expected_requests, expected_bytes ):
def CanDoWork( self, network_contexts, expected_requests = 3, expected_bytes = 1048576 ):
with self._lock:
@ -1201,19 +1228,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
for network_context in network_contexts:
bandwidth_rules = self._GetRules( network_context )
bandwidth_tracker = self._network_contexts_to_bandwidth_trackers[ network_context ]
if not bandwidth_rules.CanStartRequest( bandwidth_tracker ):
return False
return True
return self._CanStartRequest( network_contexts )
@ -1281,7 +1296,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
result = []
result = set()
for ( network_context, bandwidth_rules ) in self._network_contexts_to_bandwidth_rules.items():
if network_context.IsDefault() or network_context.IsEphemeral():
continue
# if a context has rules but no activity, list it so the user can edit the rules if needed
# in case they set too restrictive rules on an old context and now can't get it up again with activity because of the rules!
if network_context not in self._network_contexts_to_bandwidth_trackers or self._network_contexts_to_bandwidth_trackers[ network_context ].GetUsage( HC.BANDWIDTH_TYPE_REQUESTS, None ) == 0:
result.add( network_context )
for ( network_context, bandwidth_tracker ) in self._network_contexts_to_bandwidth_trackers.items():
@ -1298,7 +1329,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
result.append( network_context )
result.add( network_context )
return result
@ -1379,12 +1410,7 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
with self._lock:
for network_context in network_contexts:
self._network_contexts_to_bandwidth_trackers[ network_context ].ReportRequestUsed()
self._SetDirty()
self._ReportRequestUsed( network_contexts )
@ -1416,6 +1442,23 @@ class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
def TryToStartRequest( self, network_contexts ):
# this wraps canstart and reportrequest in one transaction to stop 5/1 rq/s happening due to race condition
with self._lock:
if not self._CanStartRequest( network_contexts ):
return False
self._ReportRequestUsed( network_contexts )
return True
def UsesDefaultRules( self, network_context ):
with self._lock:
@ -1715,6 +1758,8 @@ class NetworkEngine( object ):
else:
job.SetStatus( u'waiting for download slot\u2026' )
return True
@ -1771,6 +1816,11 @@ class NetworkJob( object ):
def __init__( self, method, url, body = None, referral_url = None, temp_path = None, for_login = False ):
if HG.network_report_mode:
HydrusData.ShowText( 'Network Job: ' + method + ' ' + url )
self.engine = None
self._lock = threading.Lock()
@ -1782,6 +1832,8 @@ class NetworkJob( object ):
self._temp_path = temp_path
self._for_login = for_login
self._creation_time = HydrusData.GetNow()
self._bandwidth_tracker = HydrusNetworking.BandwidthTracker()
self._wake_time = 0
@ -1845,8 +1897,6 @@ class NetworkJob( object ):
with self._lock:
self._ReportRequestUsed()
self._status_text = u'sending request\u2026'
@ -1984,13 +2034,6 @@ class NetworkJob( object ):
self.engine.bandwidth_manager.ReportDataUsed( self._network_contexts, num_bytes )
def _ReportRequestUsed( self ):
self._bandwidth_tracker.ReportRequestUsed()
self.engine.bandwidth_manager.ReportRequestUsed( self._network_contexts )
def _SetCancelled( self ):
self._is_cancelled = True
@ -2030,15 +2073,19 @@ class NetworkJob( object ):
if self._ObeysBandwidth():
result = self.engine.bandwidth_manager.CanStartRequest( self._network_contexts )
result = self.engine.bandwidth_manager.TryToStartRequest( self._network_contexts )
if not result:
if result:
self._bandwidth_tracker.ReportRequestUsed()
else:
waiting_duration = self.engine.bandwidth_manager.GetWaitingEstimate( self._network_contexts )
if waiting_duration <= 1:
if waiting_duration < 1:
self._status_text = ''
self._status_text = u'bandwidth free imminently\u2026'
else:
@ -2067,6 +2114,10 @@ class NetworkJob( object ):
else:
self._bandwidth_tracker.ReportRequestUsed()
self.engine.bandwidth_manager.ReportRequestUsed( self._network_contexts )
return True
@ -2122,6 +2173,14 @@ class NetworkJob( object ):
def GetCreationTime( self ):
with self._lock:
return self._creation_time
def GetErrorException( self ):
with self._lock:
@ -2213,6 +2272,11 @@ class NetworkJob( object ):
return self.engine is None
def ObeysBandwidth( self ):
return self._ObeysBandwidth()
def OverrideBandwidth( self ):
with self._lock:

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 268
SOFTWARE_VERSION = 269
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -243,6 +243,11 @@ class HydrusController( object ):
def GetBootTime( self ):
return self._timestamps[ 'boot' ]
def GetDBDir( self ):
return self.db_dir

View File

@ -1165,7 +1165,7 @@ def ToUnicode( text_producing_object ):
except:
text = repr( text ).decode( 'utf-8' )
text = unicode( repr( text ) )

View File

@ -11,6 +11,7 @@ callto_report_mode = False
db_report_mode = False
db_profile_mode = False
gui_report_mode = False
network_report_mode = False
pubsub_profile_mode = False
force_idle_mode = False
server_busy = False

View File

@ -237,6 +237,8 @@ class THREADCallToThread( DAEMON ):
except Exception as e:
HydrusData.Print( traceback.format_exc() )
HydrusData.ShowException( e )
finally:

View File

@ -452,6 +452,8 @@ class TestNetworkingJob( unittest.TestCase ):
job = self._GetJob()
job.BandwidthOK()
job.Start()
bm = job.engine.bandwidth_manager

View File

@ -624,74 +624,132 @@ class TestClientDB( unittest.TestCase ):
def test_gui_sessions( self ):
session = ClientGUIPages.GUISession( 'test_session' )
test_frame = wx.Frame( None )
gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST )
management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
session.AddPage( management_controller, [] )
service_keys_to_tags = { HydrusData.GenerateKey() : [ 'some', 'tags' ] }
management_controller = ClientGUIManagement.CreateManagementControllerImportHDD( [ 'some', 'paths' ], ClientData.ImportFileOptions(), { 'paths' : service_keys_to_tags }, True )
session.AddPage( management_controller, [] )
management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher()
session.AddPage( management_controller, [] )
management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
session.AddPage( management_controller, [] )
management_controller = ClientGUIManagement.CreateManagementControllerPetitions( CC.LOCAL_TAG_SERVICE_KEY ) # local because the controller wants to look up the service
session.AddPage( management_controller, [] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', HydrusData.GenerateKey(), fsc, True )
session.AddPage( management_controller, [] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', HydrusData.GenerateKey(), fsc, False )
session.AddPage( management_controller, [ HydrusData.GenerateKey() for i in range( 200 ) ] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.SYSTEM_PREDICATE_ARCHIVE ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', HydrusData.GenerateKey(), fsc, True )
session.AddPage( management_controller, [] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'wew lad', HydrusData.GenerateKey(), fsc, True )
session.AddPage( management_controller, [] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, ( '>', 0.2, HydrusData.GenerateKey() ) ), ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( True, HC.CONTENT_STATUS_CURRENT, HydrusData.GenerateKey() ) ) ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', HydrusData.GenerateKey(), fsc, True )
session.AddPage( management_controller, [] )
self._write( 'serialisable', session )
result = self._read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, 'test_session' )
page_names = []
for ( management_controller, initial_hashes ) in result.IteratePages():
try:
page_names.append( management_controller.GetPageName() )
session = ClientGUIPages.GUISession( 'test_session' )
#
gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST )
management_controller = ClientGUIManagement.CreateManagementControllerImportGallery( gallery_identifier )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
service_keys_to_tags = { HydrusData.GenerateKey() : [ 'some', 'tags' ] }
management_controller = ClientGUIManagement.CreateManagementControllerImportHDD( [ 'some', 'paths' ], ClientData.ImportFileOptions(), { 'paths' : service_keys_to_tags }, True )
management_controller.GetVariable( 'hdd_import' ).PausePlay() # to stop trying to import 'some' 'paths'
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher()
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
management_controller = ClientGUIManagement.CreateManagementControllerPetitions( HG.test_controller.example_tag_repo_service_key )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, predicates = [] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'search', CC.LOCAL_FILE_SERVICE_KEY, fsc, False )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [ HydrusData.GenerateKey() for i in range( 200 ) ] )
session.AddPage( page )
#
fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.SYSTEM_PREDICATE_ARCHIVE ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'wew lad', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
fsc = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, ( '>', 0.2, TestConstants.LOCAL_RATING_NUMERICAL_SERVICE_KEY ) ), ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( True, HC.CONTENT_STATUS_CURRENT, CC.LOCAL_FILE_SERVICE_KEY ) ) ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( 'files', CC.LOCAL_FILE_SERVICE_KEY, fsc, True )
page = ClientGUIPages.Page( test_frame, HG.test_controller, management_controller, [] )
session.AddPage( page )
#
self._write( 'serialisable', session )
result = self._read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, 'test_session' )
page_names = []
for ( page_type, page_data ) in result.GetPages():
if page_type == 'page':
( management_controller, initial_hashes ) = page_data
page_names.append( management_controller.GetPageName() )
self.assertEqual( page_names, [ u'hentai foundry artist', u'import', u'thread watcher', u'page download', u'example tag repo petitions', u'search', u'search', u'files', u'wew lad', u'files' ] )
finally:
test_frame.Destroy()
self.assertEqual( page_names, [ u'hentai foundry artist', u'import', u'thread watcher', u'page download', u'local tags petitions', u'search', u'search', u'files', u'wew lad', u'files' ] )
def test_import( self ):

51
test.py
View File

@ -92,6 +92,10 @@ class Controller( object ):
self._reads[ 'messaging_sessions' ] = []
self._reads[ 'tag_censorship' ] = []
self._reads[ 'options' ] = ClientDefaults.GetClientDefaultOptions()
self._reads[ 'file_system_predicates' ] = []
self._reads[ 'media_results' ] = []
self.example_tag_repo_service_key = HydrusData.GenerateKey()
services = []
@ -100,6 +104,8 @@ class Controller( object ):
services.append( ClientServices.GenerateService( CC.LOCAL_FILE_SERVICE_KEY, HC.LOCAL_FILE_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( CC.TRASH_SERVICE_KEY, HC.LOCAL_FILE_TRASH_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( CC.LOCAL_TAG_SERVICE_KEY, HC.LOCAL_TAG, CC.LOCAL_TAG_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( self.example_tag_repo_service_key, HC.TAG_REPOSITORY, 'example tag repo' ) )
services.append( ClientServices.GenerateService( CC.COMBINED_TAG_SERVICE_KEY, HC.COMBINED_TAG, CC.COMBINED_TAG_SERVICE_KEY ) )
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_LIKE_SERVICE_KEY, HC.LOCAL_RATING_LIKE, 'example local rating like service' ) )
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.LOCAL_RATING_NUMERICAL, 'example local rating numerical service' ) )
@ -197,6 +203,11 @@ class Controller( object ):
CallToThreadLongRunning = CallToThread
def DBCurrentlyDoingJob( self ):
return False
def DoHTTP( self, *args, **kwargs ): return self._http.Request( *args, **kwargs )
def GetClientSessionManager( self ):
@ -245,6 +256,11 @@ class Controller( object ):
return True
def IsCurrentPage( self, page_key ):
return False
def IsFirstStart( self ):
return True
@ -260,6 +276,16 @@ class Controller( object ):
return HG.model_shutdown
def PageCompletelyDestroyed( self, page_key ):
return False
def PageClosedButNotDestroyed( self, page_key ):
return False
def Read( self, name, *args, **kwargs ):
return self._reads[ name ]
@ -313,11 +339,25 @@ class Controller( object ):
runner.run( suite )
def SetHTTP( self, http ): self._http = http
def SetHTTP( self, http ):
self._http = http
def SetRead( self, name, value ): self._reads[ name ] = value
def SetRead( self, name, value ):
self._reads[ name ] = value
def SetWebCookies( self, name, value ): self._cookies[ name ] = value
def SetWebCookies( self, name, value ):
self._cookies[ name ] = value
def StartFileQuery( self, page_key, job_key, search_context ):
pass
def TidyUp( self ):
@ -331,6 +371,11 @@ class Controller( object ):
return HG.view_shutdown
def WaitUntilPubSubsEmpty( self ):
pass
def Write( self, name, *args, **kwargs ):
self._writes[ name ].append( ( args, kwargs ) )