Closed hssm closed 9 years ago
To investigate this issue I uninstalled the release version and executed from Eclipse
ADT... and the bug does not reproduce anymore.
Could anyone familiar with sound replay investigate this?
If the problem happens to you, please tell us here, and then try uninstalling/reinstalling
AnkiDroid.
Reported by nicolas.raoul
on 2014-03-03 05:35:43
WaitingForFeedback
This may be related to issue 2006... there seem to sometimes be missing values in the
conf dictionary, which are causing crashes. If you reproduce the crash, please open
the collection.anki2 file with an sqlite browser like this
(https://play.google.com/store/apps/details?id=com.xuecs.sqlitemanager&hl=en)
and go to "col" table -> "dconf" field -> and check if "replayq" is missing from any
of the models.
Reported by perceptualchaos2
on 2014-03-03 08:24:36
Oh hey! I think I know what this is from. I made an incidental change that was merged
here: https://github.com/ankidroid/Anki-Android/commit/d529c353b82644ed787a2dd5eb83e1b262ba7468
It just makes the UI more consistent, in that it disables a checkbox not applicable
to the user under those conditions, but I'm thinking that the disabling of the checkbox
is keeping the preference's state from being accessed in the code, leading to a null
value of sorts. When you re-installed, that would provide fresh preferences, so this
would not affect fresh installs, only upgrades, assuming I've got it pegged right,
which I classify as likely.
I had only expected the checkbox to be enabled/disabled, but I didn't think the actual
state would be unobtainable. Maybe if the checkbox is disabled programatically, rather
than using android:dependency in deck_options.xml
Reported by chajadan
on 2014-03-03 08:28:37
Maybe the issue got fixed because reinstalling forced a "Checking database" (which took
a long time but shrunk by zero).
I had tried with several decks using several models, all caused this crash.
Reported by nicolas.raoul
on 2014-03-03 08:28:38
I don't remember having opened the preferences screen since a few weeks, if that matters.
@chajadan: Would you have time to try and fix it? Thanks a lot :-)
Reported by nicolas.raoul
on 2014-03-03 08:34:15
Well I have a two definite fix options (I believe) and then a better fix (codewise).
Obvious fix number 1: just don't disable that checkbox. This would work but is not
UI one would shot for.
A fix I trust to work: in commit(), set the value to false at the front of the function,
and if it seems a value of true (which means the preference is enabled) let it update
the value as it normally would later in that function in the loop. I trust this would
work, but it's a bit unclean codewise to have this sole exception isolated from how
everything else works.
What would be nicest is to just not use dependency, and then programatically disable
the checkbox when rendering preferences. However, I have no reason to believe this
would work any better. For all I know, android:dependency is equivalent to enabling/disabling
the view, and my research does not indicate it affects access of the dependent preference's
state. So I don't trust this, nor understand the "PreferenceHack".
But the first two I trust would handle this. What would you shoot for? I'm thinking
option two is the best compromise, when option three is seeking perfection I can't
offer at the moment.
Reported by chajadan
on 2014-03-03 08:48:36
How about option 2 and create an enhancement for the ideal behavior your described?
Since we are approaching release, let's be on the safe side. Even 1 would be OK actually,
if it the safest.
Reported by nicolas.raoul
on 2014-03-03 08:55:14
another option would be to surround access of the JSON object with a try block, but
that leaves holes in other accesses that exist or spring up
Reported by chajadan
on 2014-03-03 08:55:39
If the desktop accesses it with .get() it means it was not in the initial release of
Anki 2, and AnkiDroid should fall back to a default value when it doesn't exist as
well.
Reported by dae@ichi2.net
on 2014-03-03 12:49:02
If we fall back to default values for any parameters in the collection which were not
in the initial release of Anki2, then would it be worth throwing a warning that the
value was not found, to aid with debugging behavioral problems!
There could be some underlying problem for why the value was not there, other than
the collection simply having originated from an older version of Anki.
Reported by perceptualchaos2
on 2014-03-05 04:52:11
Sorry, I don't think that would be useful. Such variables may or may not be defined,
and it's not a sign of a problem if they haven't been set yet - it simply indicates
that the user has not customized the value since upgrading.
Reported by dae@ichi2.net
on 2014-03-05 05:04:39
Yeah probably not... but at least I think we need to write something in the comments
every-time we include a default value in the get() call...
P.S. I'm sure it was obvious, but I meant a programmatic warning visible in the log-file,
rather than a warning shown to the user.
Reported by perceptualchaos2
on 2014-03-05 06:11:42
In 2.1beta6: http://ankidroid-triage.appspot.com/view_crash?crash_id=4610053478809600
Reported by nicolas.raoul
on 2014-03-07 16:21:18
Accepted
I fixed the replayq crash with this commit:
https://github.com/nicolas-raoul/Anki-Android/commit/3792faa1d362a5eb723aff35c2841ef95eb2f14c
A method getBoolean(JSONObject, Boolean defaultValue) would be cleaner, let's see what
other values need the same thing before deciding what is the best way to improve this.
So, what are the other values that can be absent?
All of defaultConf in decks.py?
Reported by nicolas.raoul
on 2014-03-09 15:01:56
Only attributes that are referenced in the code with thedeck.get() instead of thedeck[attr]
Reported by dae@ichi2.net
on 2014-03-10 02:45:44
One way to handle the issue of new options that will be lacking from the database would
be to use the upgrade paths. When a new setting is introduced, simply write a default
to the database if relevant to the upgrade at hand. Before releasing v2.1 we could
do a one time catch-up upgrade that ensures all current options have at least a default
value present, and that would avoid the access crashes.
Reported by chajadan
on 2014-03-16 09:00:10
That is not a robust way to handle it, as a user could sync an older client and undo
the upgraded changes. IMHO the best way to handle it is to access those variables conditionally,
which should be happening already if the desktop code has been ported without modifications.
Reported by dae@ichi2.net
on 2014-03-16 10:28:04
OK so to proceed with this, we need to make a list of all the attributes in desktop
which are referenced by deck.get()? Is this something we want to include in 2.1?
Reported by perceptualchaos2
on 2014-03-18 10:15:08
In Anki Desktop source code:
find . -name "*.py" | xargs grep -R "\.get(" * |grep -v "js.py"
anki/notes.py: self._model = self.col.models.get(self.mid)
anki/importing/supermemo_xml.py: x.get('first_name').
anki/importing/anki2.py: self.col.models.get(oldMid)['name'],
anki/importing/anki2.py: srcModel = self.src.models.get(srcMid)
anki/importing/anki2.py: dstModel = self.dst.models.get(mid)
anki/importing/anki2.py: g = self.src.decks.get(did)
anki/importing/anki2.py: g2 = self.dst.decks.get(newid)
anki/importing/anki2.py: deck = self.dst.decks.get(newid)
anki/importing/anki1.py: d = self.col.decks.get(did)
anki/importing/mnemo.py: fld = self._mungeField(orig.get(f, ''))
anki/importing/mnemo.py: n.cards = orig.get('cards', {})
anki/importing/mnemo.py: fld = orig.get("text", "")
anki/importing/mnemo.py: n.cards = orig.get('cards', {})
Binary file anki/importing/supermemo_xml.pyc matches
anki/sync.py: self.uname = meta.get("uname", "")
anki/sync.py: l = self.col.models.get(r['id'])
anki/sync.py: l = self.col.decks.get(r['id'], False)
anki/db.py: self.echo = os.environ.get("DBECHO")
anki/template/view.py: val = self.get(attr, None)
anki/template/view.py: attr = self.context.get(attr, getattr(self, attr, default))
anki/decks.py: deck = self.get(did)
anki/decks.py: deck = self.get(did)
anki/decks.py: deck = self.get(did)
anki/decks.py: deck = self.get(did)
anki/decks.py: collapsed = deck.get('browserCollapsed', False)
anki/decks.py: draggedDeck = self.get(draggedDeckDid)
anki/decks.py: ontoDeckName = self.get(ontoDeckDid)['name']
anki/decks.py: draggedDeck = self.get(draggedDeckDid)
anki/decks.py: ontoDeckName = self.get(ontoDeckDid)['name']
anki/decks.py: deck = self.get(did, default=False)
anki/decks.py: deck = self.get(did, default=default)
anki/decks.py: deck = self.get(did, default=False)
anki/decks.py: return self.get(self.selected())
anki/decks.py: name = self.get(did)['name']
anki/decks.py: for part in self.get(did)['name'].split("::")[:-1]:
anki/decks.py: parents[c] = self.get(self.id(p))
anki/decks.py: return self.get(did)['dyn']
anki/collection.py: if not self.conf.get("newBury", False):
anki/collection.py: id = self.conf.get(type, 1)
anki/collection.py: model = self.models.get(mid)
anki/collection.py: did = dids.get(nid) or model['did']
anki/collection.py: did = self.decks.get(did)['id']
anki/collection.py: deck = self.decks.get(card.did)
anki/collection.py: model = self.models.get(mid)
anki/collection.py: model = self.models.get(data[2])
anki/collection.py: limit=2+kwargs.get("stack", 0))[0]
anki/collection.py: if os.environ.get("ANKIDEV"):
anki/cards.py: args = (t.get('bqfmt'), t.get('bafmt'))
anki/cards.py: return self.col.models.get(self.note().mid)
anki/media.py: model = self.col.models.get(mid)
anki/storage.py: r['ivlFct'] = r.get("ivlfct", 1)
anki/sched.py: for g in ([self.col.decks.get(card.did)] +
anki/sched.py: children = [self.col.decks.get(did) for (name, did) in
anki/sched.py: lim = limFn(self.col.decks.get(did))
anki/sched.py: deck = self.col.decks.get(did)
anki/sched.py: sel = self.col.decks.get(did)
anki/sched.py: if self.col.decks.get(did)['dyn']:
anki/sched.py: new = ivl * conf.get('ivlFct', 1)
anki/sched.py: deck = self.col.decks.get(did)
anki/sched.py: deck = self.col.decks.get(did)
anki/sched.py: bury=oconf['new'].get("bury", True),
anki/sched.py: unburied = self.col.conf.get("lastUnburied", 0)
anki/sched.py: buryNew = nconf.get("bury", True)
anki/sched.py: buryRev = rconf.get("bury", True)
anki/find.py: model = col.models.get(mid)
anki/hooks.py: hook = _hooks.get(hook, None)
anki/hooks.py: hook = _hooks.get(hook, None)
anki/hooks.py: if not _hooks.get(hook, None):
anki/hooks.py: hook = _hooks.get(hook, [])
anki/models.py: m = self.get(self.col.decks.current().get('mid'))
anki/models.py: m = self.get(self.col.conf['curModel'])
anki/models.py: flds.append(newflds.get(c, ""))
anki/consts.py:SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/"
aqt/clayout.py: f.qfmt.setText(t.get('bqfmt', ""))
aqt/clayout.py: f.afmt.setText(t.get('bafmt', ""))
aqt/clayout.py: f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
aqt/clayout.py: f.fontSize.setValue(t.get('bsize', 12))
aqt/clayout.py: te.setText(self.col.decks.get(t['did'])['name'])
aqt/deckbrowser.py: self.mw.pm.profile.get("hideDeckLotsMsg")):
aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
aqt/deckbrowser.py: if self.mw.col.decks.get(did)['collapsed']:
aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
aqt/deckconf.py: deck = self.mw.col.decks.get(did)
aqt/deckconf.py: f.bury.setChecked(c.get("bury", True))
aqt/deckconf.py: f.buryRev.setChecked(c.get("bury", True))
aqt/deckconf.py: f.showTimer.setChecked(c.get('timer', 0))
aqt/deckconf.py: f.replayQuestion.setChecked(c.get('replayq', True))
aqt/upgrade.py: loc = self.oldprefs.get("mediaLocation")
aqt/browser.py: self.activeCols = self.col.conf.get(
aqt/browser.py: f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
aqt/browser.py: f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
aqt/browser.py: if c.template().get('bafmt'):
aqt/browser.py: bsize = t.get("bsize", 0)
aqt/browser.py: collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
aqt/browser.py: current=self.mw.col.decks.get(did)['name']
aqt/browser.py: deck = self.col.decks.get(did)
aqt/browser.py: self.oldModel = self.browser.col.models.get(
aqt/editor.py: self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
aqt/editor.py: if self.editor.mw.pm.profile.get("pastePNG", False):
aqt/importing.py: self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
aqt/importing.py: self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
aqt/importing.py: if self.mw.col.conf.get("addToCur", True):
aqt/deckchooser.py: if self.mw.col.conf.get("addToCur", True):
aqt/deckchooser.py: if not self.mw.col.conf.get("addToCur", True):
aqt/taglimit.py: yes = self.deck.get("activeTags", [])
aqt/taglimit.py: no = self.deck.get("inactiveTags", [])
aqt/main.py: if self.pm.profile.get('compressBackups', True):
aqt/main.py: return self.col.decks.get(did)
aqt/main.py: if self.pm.meta.get('suppressUpdate', None) != ver:
aqt/overview.py: self.sid = deck.get("sharedFrom")
aqt/overview.py: self.sidVer = deck.get("ver", None)
aqt/overview.py: desc = deck.get("desc", "")
aqt/qt.py:if os.environ.get("DEBUG"):
aqt/customstudy.py: sval = min(new, self.deck.get('extendNew', 10))
aqt/customstudy.py: sval = min(rev, self.deck.get('extendRev', 10))
aqt/customstudy.py: dyn = self.mw.col.decks.get(did)
aqt/models.py: m = AddModel(self.mw, self).get()
aqt/utils.py: dir = aqt.mw.pm.profile.get(dirkey, "")
aqt/utils.py: base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
aqt/utils.py: if aqt.mw.pm.profile.get(key):
aqt/utils.py: if aqt.mw.pm.profile.get(key):
aqt/utils.py: if aqt.mw.pm.profile.get(key):
aqt/utils.py: if aqt.mw.pm.profile.get(key):
aqt/preferences.py: f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
aqt/preferences.py: self.form.syncUser.setText(self.prof.get('syncUser',
""))
aqt/preferences.py: self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
aqt/preferences.py: self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
aqt/reviewer.py: self.card.odid or self.card.did).get('replayq', True)
aqt/reviewer.py: self.mw.onDeckConf(self.mw.col.decks.get(
oldanki/sync.py: mod = self.deck.s.query(Model).get(id)
oldanki/stats.py: boxes[n] = boxes.get(n, 0) + 1
oldanki/deck.py: card = self.s.query(oldanki.cards.Card).get(id)
oldanki/deck.py: model = self.s.query(Model).get(oldFact.model.id)
oldanki/deck.py: deck = s.query(Deck).get(1)
oldanki/template/view.py: val = self.get(attr, None)
oldanki/template/view.py: attr = self.context.get(attr, getattr(self, attr,
default))
oldanki/stdmodels.py: fn = models.get(name)
oldanki/hooks.py: hook = _hooks.get(hook, None)
oldanki/hooks.py: hook = _hooks.get(hook, None)
oldanki/hooks.py: if not _hooks.get(hook, None):
oldanki/hooks.py: hook = _hooks.get(hook, [])
oldanki/hooks.py: return not _hooks.get(hook)
oldanki/graphs.py: days[day] = days.get(day, 0) + 1
oldanki/graphs.py: next[indays] = next.get(indays, 0) + 1 # type-agnostic
stats
oldanki/graphs.py: dest[indays] = dest.get(indays, 0) + 1 # type-specific
stats
oldanki/graphs.py: days[d] = days.get(d, 0) + 1
tests/test_exporting.py: dobj = deck.decks.get(did)
tests/test_exporting.py: dobj = d2.decks.get(did)
tests/test_decks.py: d.decks.rename(d.decks.get(id), "foo::bar")
tests/test_decks.py: Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
tests/test_decks.py: d.decks.rename(d.decks.get(id), "yo")
tests/test_sync.py: assert deck2.models.get(cm['id'])['name'].startswith("Basic")
tests/test_sync.py: assert deck2.models.get(cm['id'])['name'] == "new"
tests/test_sched.py: d.decks.setConf(d.decks.get(g2), c2)
tests/test_sched.py: cram = d.decks.get(did)
tests/test_sched.py: cram = d.decks.get(did)
thirdparty/send2trash/plat_other.py:XDG_DATA_HOME = op.expanduser(os.environ.get(u'XDG_DATA_HOME',
u'~/.local/share'))
thirdparty/httplib2/__init__.py: hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
thirdparty/httplib2/__init__.py: encoding = response.get('content-encoding',
None)
thirdparty/httplib2/__init__.py: raise FailedToDecompressContent(_("Content
purported to be compressed with %s but failed to decompress.") % response.get('content-encoding'),
response, content)
thirdparty/httplib2/__init__.py: vary = response_headers.get('vary', None)
thirdparty/httplib2/__init__.py: qop = self.challenge.get('qop', 'auth')
thirdparty/httplib2/__init__.py: self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
thirdparty/httplib2/__init__.py: if self.challenge.get('opaque'):
thirdparty/httplib2/__init__.py: challenge = _parse_www_authenticate(response,
'www-authenticate').get('digest', {})
thirdparty/httplib2/__init__.py: if 'true' == challenge.get('stale'):
thirdparty/httplib2/__init__.py: updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
thirdparty/httplib2/__init__.py: self.challenge['reason'] = self.challenge.get('reason',
'unauthorized')
thirdparty/httplib2/__init__.py: self.challenge['salt'] = self.challenge.get('salt',
'')
thirdparty/httplib2/__init__.py: if not self.challenge.get('snonce'):
thirdparty/httplib2/__init__.py: self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
thirdparty/httplib2/__init__.py: self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
thirdparty/httplib2/__init__.py: challenge = _parse_www_authenticate(response,
'www-authenticate').get('hmacdigest', {})
thirdparty/httplib2/__init__.py: if challenge.get('reason') in ['integrity',
'stale']:
thirdparty/httplib2/__init__.py: service = challenge['googlelogin'].get('service',
'xapi')
thirdparty/httplib2/__init__.py: url = os.environ.get(env_var, os.environ.get(env_var.upper()))
thirdparty/httplib2/__init__.py: no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
thirdparty/httplib2/__init__.py: self.response['reason'] = httplib.responses.get(response.status_code,
'Ok')
thirdparty/httplib2/__init__.py: cached_value = self.cache.get(cachekey)
thirdparty/httplib2/__init__.py: if headers.get(header, None) !=
value:
thirdparty/httplib2/__init__.py: self.status = int(self.get('status', self.status))
thirdparty/httplib2/__init__.py: self.reason = self.get('reason', self.reason)
thirdparty/BeautifulSoup.py: return self._getAttrMap().get(key, default)
thirdparty/BeautifulSoup.py: attrValue = markupAttrMap.get(attr)
thirdparty/BeautifulSoup.py: nestingResetTriggers = self.NESTABLE_TAGS.get(name)
thirdparty/BeautifulSoup.py: data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
thirdparty/BeautifulSoup.py: not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
thirdparty/BeautifulSoup.py: sub = self.MS_CHARS.get(orig)
thirdparty/BeautifulSoup.py: return self._codec(self.CHARSET_ALIASES.get(charset,
charset)) \
./oldanki/sync.py: mod = self.deck.s.query(Model).get(id)
./oldanki/stats.py: boxes[n] = boxes.get(n, 0) + 1
./oldanki/deck.py: card = self.s.query(oldanki.cards.Card).get(id)
./oldanki/deck.py: model = self.s.query(Model).get(oldFact.model.id)
./oldanki/deck.py: deck = s.query(Deck).get(1)
./oldanki/template/view.py: val = self.get(attr, None)
./oldanki/template/view.py: attr = self.context.get(attr, getattr(self, attr,
default))
./oldanki/stdmodels.py: fn = models.get(name)
./oldanki/hooks.py: hook = _hooks.get(hook, None)
./oldanki/hooks.py: hook = _hooks.get(hook, None)
./oldanki/hooks.py: if not _hooks.get(hook, None):
./oldanki/hooks.py: hook = _hooks.get(hook, [])
./oldanki/hooks.py: return not _hooks.get(hook)
./oldanki/graphs.py: days[day] = days.get(day, 0) + 1
./oldanki/graphs.py: next[indays] = next.get(indays, 0) + 1 # type-agnostic
stats
./oldanki/graphs.py: dest[indays] = dest.get(indays, 0) + 1 # type-specific
stats
./oldanki/graphs.py: days[d] = days.get(d, 0) + 1
./thirdparty/send2trash/plat_other.py:XDG_DATA_HOME = op.expanduser(os.environ.get(u'XDG_DATA_HOME',
u'~/.local/share'))
./thirdparty/httplib2/__init__.py: hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
./thirdparty/httplib2/__init__.py: encoding = response.get('content-encoding',
None)
./thirdparty/httplib2/__init__.py: raise FailedToDecompressContent(_("Content
purported to be compressed with %s but failed to decompress.") % response.get('content-encoding'),
response, content)
./thirdparty/httplib2/__init__.py: vary = response_headers.get('vary', None)
./thirdparty/httplib2/__init__.py: qop = self.challenge.get('qop', 'auth')
./thirdparty/httplib2/__init__.py: self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
./thirdparty/httplib2/__init__.py: if self.challenge.get('opaque'):
./thirdparty/httplib2/__init__.py: challenge = _parse_www_authenticate(response,
'www-authenticate').get('digest', {})
./thirdparty/httplib2/__init__.py: if 'true' == challenge.get('stale'):
./thirdparty/httplib2/__init__.py: updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
./thirdparty/httplib2/__init__.py: self.challenge['reason'] = self.challenge.get('reason',
'unauthorized')
./thirdparty/httplib2/__init__.py: self.challenge['salt'] = self.challenge.get('salt',
'')
./thirdparty/httplib2/__init__.py: if not self.challenge.get('snonce'):
./thirdparty/httplib2/__init__.py: self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
./thirdparty/httplib2/__init__.py: self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
./thirdparty/httplib2/__init__.py: challenge = _parse_www_authenticate(response,
'www-authenticate').get('hmacdigest', {})
./thirdparty/httplib2/__init__.py: if challenge.get('reason') in ['integrity',
'stale']:
./thirdparty/httplib2/__init__.py: service = challenge['googlelogin'].get('service',
'xapi')
./thirdparty/httplib2/__init__.py: url = os.environ.get(env_var, os.environ.get(env_var.upper()))
./thirdparty/httplib2/__init__.py: no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
./thirdparty/httplib2/__init__.py: self.response['reason'] = httplib.responses.get(response.status_code,
'Ok')
./thirdparty/httplib2/__init__.py: cached_value = self.cache.get(cachekey)
./thirdparty/httplib2/__init__.py: if headers.get(header, None)
!= value:
./thirdparty/httplib2/__init__.py: self.status = int(self.get('status',
self.status))
./thirdparty/httplib2/__init__.py: self.reason = self.get('reason', self.reason)
./thirdparty/BeautifulSoup.py: return self._getAttrMap().get(key, default)
./thirdparty/BeautifulSoup.py: attrValue = markupAttrMap.get(attr)
./thirdparty/BeautifulSoup.py: nestingResetTriggers = self.NESTABLE_TAGS.get(name)
./thirdparty/BeautifulSoup.py: data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
./thirdparty/BeautifulSoup.py: not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
./thirdparty/BeautifulSoup.py: sub = self.MS_CHARS.get(orig)
./thirdparty/BeautifulSoup.py: return self._codec(self.CHARSET_ALIASES.get(charset,
charset)) \
./anki/notes.py: self._model = self.col.models.get(self.mid)
./anki/importing/supermemo_xml.py: x.get('first_name').
./anki/importing/anki2.py: self.col.models.get(oldMid)['name'],
./anki/importing/anki2.py: srcModel = self.src.models.get(srcMid)
./anki/importing/anki2.py: dstModel = self.dst.models.get(mid)
./anki/importing/anki2.py: g = self.src.decks.get(did)
./anki/importing/anki2.py: g2 = self.dst.decks.get(newid)
./anki/importing/anki2.py: deck = self.dst.decks.get(newid)
./anki/importing/anki1.py: d = self.col.decks.get(did)
./anki/importing/mnemo.py: fld = self._mungeField(orig.get(f, ''))
./anki/importing/mnemo.py: n.cards = orig.get('cards', {})
./anki/importing/mnemo.py: fld = orig.get("text", "")
./anki/importing/mnemo.py: n.cards = orig.get('cards', {})
./anki/sync.py: self.uname = meta.get("uname", "")
./anki/sync.py: l = self.col.models.get(r['id'])
./anki/sync.py: l = self.col.decks.get(r['id'], False)
./anki/db.py: self.echo = os.environ.get("DBECHO")
./anki/template/view.py: val = self.get(attr, None)
./anki/template/view.py: attr = self.context.get(attr, getattr(self, attr, default))
./anki/decks.py: deck = self.get(did)
./anki/decks.py: deck = self.get(did)
./anki/decks.py: deck = self.get(did)
./anki/decks.py: deck = self.get(did)
./anki/decks.py: collapsed = deck.get('browserCollapsed', False)
./anki/decks.py: draggedDeck = self.get(draggedDeckDid)
./anki/decks.py: ontoDeckName = self.get(ontoDeckDid)['name']
./anki/decks.py: draggedDeck = self.get(draggedDeckDid)
./anki/decks.py: ontoDeckName = self.get(ontoDeckDid)['name']
./anki/decks.py: deck = self.get(did, default=False)
./anki/decks.py: deck = self.get(did, default=default)
./anki/decks.py: deck = self.get(did, default=False)
./anki/decks.py: return self.get(self.selected())
./anki/decks.py: name = self.get(did)['name']
./anki/decks.py: for part in self.get(did)['name'].split("::")[:-1]:
./anki/decks.py: parents[c] = self.get(self.id(p))
./anki/decks.py: return self.get(did)['dyn']
./anki/collection.py: if not self.conf.get("newBury", False):
./anki/collection.py: id = self.conf.get(type, 1)
./anki/collection.py: model = self.models.get(mid)
./anki/collection.py: did = dids.get(nid) or model['did']
./anki/collection.py: did = self.decks.get(did)['id']
./anki/collection.py: deck = self.decks.get(card.did)
./anki/collection.py: model = self.models.get(mid)
./anki/collection.py: model = self.models.get(data[2])
./anki/collection.py: limit=2+kwargs.get("stack", 0))[0]
./anki/collection.py: if os.environ.get("ANKIDEV"):
./anki/cards.py: args = (t.get('bqfmt'), t.get('bafmt'))
./anki/cards.py: return self.col.models.get(self.note().mid)
./anki/media.py: model = self.col.models.get(mid)
./anki/storage.py: r['ivlFct'] = r.get("ivlfct", 1)
./anki/sched.py: for g in ([self.col.decks.get(card.did)] +
./anki/sched.py: children = [self.col.decks.get(did) for (name, did) in
./anki/sched.py: lim = limFn(self.col.decks.get(did))
./anki/sched.py: deck = self.col.decks.get(did)
./anki/sched.py: sel = self.col.decks.get(did)
./anki/sched.py: if self.col.decks.get(did)['dyn']:
./anki/sched.py: new = ivl * conf.get('ivlFct', 1)
./anki/sched.py: deck = self.col.decks.get(did)
./anki/sched.py: deck = self.col.decks.get(did)
./anki/sched.py: bury=oconf['new'].get("bury", True),
./anki/sched.py: unburied = self.col.conf.get("lastUnburied", 0)
./anki/sched.py: buryNew = nconf.get("bury", True)
./anki/sched.py: buryRev = rconf.get("bury", True)
./anki/find.py: model = col.models.get(mid)
./anki/hooks.py: hook = _hooks.get(hook, None)
./anki/hooks.py: hook = _hooks.get(hook, None)
./anki/hooks.py: if not _hooks.get(hook, None):
./anki/hooks.py: hook = _hooks.get(hook, [])
./anki/models.py: m = self.get(self.col.decks.current().get('mid'))
./anki/models.py: m = self.get(self.col.conf['curModel'])
./anki/models.py: flds.append(newflds.get(c, ""))
./anki/consts.py:SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/"
./tests/test_exporting.py: dobj = deck.decks.get(did)
./tests/test_exporting.py: dobj = d2.decks.get(did)
./tests/test_decks.py: d.decks.rename(d.decks.get(id), "foo::bar")
./tests/test_decks.py: Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
./tests/test_decks.py: d.decks.rename(d.decks.get(id), "yo")
./tests/test_sync.py: assert deck2.models.get(cm['id'])['name'].startswith("Basic")
./tests/test_sync.py: assert deck2.models.get(cm['id'])['name'] == "new"
./tests/test_sched.py: d.decks.setConf(d.decks.get(g2), c2)
./tests/test_sched.py: cram = d.decks.get(did)
./tests/test_sched.py: cram = d.decks.get(did)
./aqt/clayout.py: f.qfmt.setText(t.get('bqfmt', ""))
./aqt/clayout.py: f.afmt.setText(t.get('bafmt', ""))
./aqt/clayout.py: f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
./aqt/clayout.py: f.fontSize.setValue(t.get('bsize', 12))
./aqt/clayout.py: te.setText(self.col.decks.get(t['did'])['name'])
./aqt/deckbrowser.py: self.mw.pm.profile.get("hideDeckLotsMsg")):
./aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
./aqt/deckbrowser.py: if self.mw.col.decks.get(did)['collapsed']:
./aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
./aqt/deckbrowser.py: deck = self.mw.col.decks.get(did)
./aqt/deckconf.py: deck = self.mw.col.decks.get(did)
./aqt/deckconf.py: f.bury.setChecked(c.get("bury", True))
./aqt/deckconf.py: f.buryRev.setChecked(c.get("bury", True))
./aqt/deckconf.py: f.showTimer.setChecked(c.get('timer', 0))
./aqt/deckconf.py: f.replayQuestion.setChecked(c.get('replayq', True))
./aqt/upgrade.py: loc = self.oldprefs.get("mediaLocation")
./aqt/browser.py: self.activeCols = self.col.conf.get(
./aqt/browser.py: f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
./aqt/browser.py: f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
./aqt/browser.py: if c.template().get('bafmt'):
./aqt/browser.py: bsize = t.get("bsize", 0)
./aqt/browser.py: collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
./aqt/browser.py: current=self.mw.col.decks.get(did)['name']
./aqt/browser.py: deck = self.col.decks.get(did)
./aqt/browser.py: self.oldModel = self.browser.col.models.get(
./aqt/editor.py: self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
./aqt/editor.py: if self.editor.mw.pm.profile.get("pastePNG", False):
./aqt/importing.py: self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
./aqt/importing.py: self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
./aqt/importing.py: if self.mw.col.conf.get("addToCur", True):
./aqt/deckchooser.py: if self.mw.col.conf.get("addToCur", True):
./aqt/deckchooser.py: if not self.mw.col.conf.get("addToCur", True):
./aqt/taglimit.py: yes = self.deck.get("activeTags", [])
./aqt/taglimit.py: no = self.deck.get("inactiveTags", [])
./aqt/main.py: if self.pm.profile.get('compressBackups', True):
./aqt/main.py: return self.col.decks.get(did)
./aqt/main.py: if self.pm.meta.get('suppressUpdate', None) != ver:
./aqt/overview.py: self.sid = deck.get("sharedFrom")
./aqt/overview.py: self.sidVer = deck.get("ver", None)
./aqt/overview.py: desc = deck.get("desc", "")
./aqt/qt.py:if os.environ.get("DEBUG"):
./aqt/customstudy.py: sval = min(new, self.deck.get('extendNew', 10))
./aqt/customstudy.py: sval = min(rev, self.deck.get('extendRev', 10))
./aqt/customstudy.py: dyn = self.mw.col.decks.get(did)
./aqt/models.py: m = AddModel(self.mw, self).get()
./aqt/utils.py: dir = aqt.mw.pm.profile.get(dirkey, "")
./aqt/utils.py: base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
./aqt/utils.py: if aqt.mw.pm.profile.get(key):
./aqt/utils.py: if aqt.mw.pm.profile.get(key):
./aqt/utils.py: if aqt.mw.pm.profile.get(key):
./aqt/utils.py: if aqt.mw.pm.profile.get(key):
./aqt/preferences.py: f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
./aqt/preferences.py: self.form.syncUser.setText(self.prof.get('syncUser',
""))
./aqt/preferences.py: self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
./aqt/preferences.py: self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
./aqt/reviewer.py: self.card.odid or self.card.did).get('replayq', True)
./aqt/reviewer.py: self.mw.onDeckConf(self.mw.col.decks.get(
Not sure how to filter only those that are called on an object of type deck... many
seem to be other data structures.
Reported by nicolas.raoul
on 2014-03-18 10:20:26
I tried with the regex: \.get\([\"\'].*[\"\'],.*
Name Line Text
collection.py 75 if not self.conf.get("newBury", False):
collection.py 803 limit=2+kwargs.get("stack", 0))[0]
decks.py 202 collapsed = deck.get('browserCollapsed', False)
mnemo.py 115 n.cards = orig.get('cards', {})
mnemo.py 166 fld = orig.get("text", "")
mnemo.py 179 n.cards = orig.get('cards', {})
sched.py 920 new = ivl * conf.get('ivlFct', 1)
sched.py 1082 bury=oconf['new'].get("bury", True),
sched.py 1147 unburied = self.col.conf.get("lastUnburied", 0)
sched.py 1300 buryNew = nconf.get("bury", True)
sched.py 1302 buryRev = rconf.get("bury", True)
storage.py 172 r['ivlFct'] = r.get("ivlfct", 1)
sync.py 123 self.uname = meta.get("uname", "")
browser.py 80 f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
browser.py 81 f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
browser.py 453 bsize = t.get("bsize", 0)
browser.py 844 collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
clayout.py 356 f.qfmt.setText(t.get('bqfmt', ""))
clayout.py 357 f.afmt.setText(t.get('bafmt', ""))
clayout.py 358 f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
clayout.py 359 f.fontSize.setValue(t.get('bsize', 12))
customstudy.py 59 sval = min(new, self.deck.get('extendNew', 10))
customstudy.py 65 sval = min(rev, self.deck.get('extendRev', 10))
deckchooser.py 35 if self.mw.col.conf.get("addToCur", True):
deckchooser.py 69 if not self.mw.col.conf.get("addToCur", True):
deckconf.py 183 f.bury.setChecked(c.get("bury", True))
deckconf.py 192 f.buryRev.setChecked(c.get("bury", True))
deckconf.py 203 f.showTimer.setChecked(c.get('timer', 0))
deckconf.py 205 f.replayQuestion.setChecked(c.get('replayq', True))
editor.py 722 self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
editor.py 1183 if self.editor.mw.pm.profile.get("pastePNG", False):
importing.py 80 self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
importing.py 81 self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
importing.py 97 if self.mw.col.conf.get("addToCur", True):
main.py 329 if self.pm.profile.get('compressBackups', True):
main.py 828 if self.pm.meta.get('suppressUpdate', None) != ver:
overview.py 90 self.sidVer = deck.get("ver", None)
overview.py 112 desc = deck.get("desc", "")
preferences.py 56 f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
preferences.py 86 self.form.syncUser.setText(self.prof.get('syncUser',
""))
preferences.py 115 self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
preferences.py 132 self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
reviewer.py 223 self.card.odid or self.card.did).get('replayq', True)
taglimit.py 23 yes = self.deck.get("activeTags", [])
taglimit.py 24 no = self.deck.get("inactiveTags", [])
__init__.py 194 hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
__init__.py 388 encoding = response.get('content-encoding', None)
__init__.py 417 vary = response_headers.get('vary', None)
__init__.py 507 qop = self.challenge.get('qop', 'auth')
__init__.py 511 self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
__init__.py 545 challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest',
{})
__init__.py 551 updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
__init__.py 568 self.challenge['reason'] = self.challenge.get('reason', 'unauthorized')
__init__.py 571 self.challenge['salt'] = self.challenge.get('salt', '')
__init__.py 574 self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
__init__.py 577 self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
__init__.py 615 challenge = _parse_www_authenticate(response, 'www-authenticate').get('hmacdigest',
{})
__init__.py 616 if challenge.get('reason') in ['integrity', 'stale']:
__init__.py 650 service = challenge['googlelogin'].get('service', 'xapi')
__init__.py 795 no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
__init__.py 1676 self.status = int(self.get('status', self.status))
__init__.py 1677 self.reason = self.get('reason', self.reason)
Reported by perceptualchaos2
on 2014-03-18 16:48:31
If you look how this is implemented in libanki, JSON objects have methods which seems
to do exactly what we need:
http://www.json.org/javadoc/org/json/JSONObject.html#optBoolean(java.lang.String, boolean)
optBoolean(java.lang.String key, boolean defaultValue)
optInt(java.lang.String key, int defaultValue)
optString(java.lang.String key, java.lang.String defaultValue)
I've gone through all of the values in the above GREP, and I think these are the only
places that may require a default value to be included:
\libanki\Sched.javaSched.java(1532) newIvl = ivl * conf.getDouble("ivlFct");
libanki\Card.java 279 args.add(t.getString("bqfmt"));
libanki\Card.java 280 args.add(t.getString("bafmt"));
libanki\Card.java 687 return mCol.getDecks().confForDid(mODid == 0 ? mDid : mODid).getInt("timer")
!= 0;
libanki\Decks.java 817 return current().getString("desc");
Preferences.java 178 useCurrent.setValueIndex(conf.getBoolean("addToCur")
? 0 : 1);
Reported by perceptualchaos2
on 2014-03-18 18:33:40
OK this was actually a lot less work than I expected:
I've split what I've done into 2 pull requests:
213 - which just solves the replayq issue in a cleaner way:
https://github.com/ankidroid/Anki-Android/pull/213/files
and 214 - which adds default values for the other parameters in comment 21 (except
bqfmt and bafmt which seem unnecessary):
https://github.com/ankidroid/Anki-Android/pull/214/files
It would be very helpful if someone could check if there were any values which were
missed.
Reported by perceptualchaos2
on 2014-03-18 19:41:29
FixedInDev
Fixed in version 2.1.1, available on Google Play
Reported by nicolas.raoul
on 2014-03-28 08:19:22
Fixed
Issue 1649 has been merged into this issue.
Reported by Houssam.Salem.Au
on 2014-03-31 05:44:53
Originally reported on Google Code with ID 2005
Reported by
nicolas.raoul
on 2014-03-02 03:57:21