Merge pull request #4220 from PastaPastaPasta/backport-triv-pr4

backport: 'trivial' pr4
This commit is contained in:
UdjinM6 2021-07-02 00:39:13 +03:00 committed by GitHub
commit e6d008d385
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 175 additions and 56 deletions

View File

@ -101,6 +101,8 @@ At this stage one should expect comments and review from other contributors. You
can add more commits to your pull request by committing them locally and pushing
to your fork until you have satisfied all feedback.
Note: Code review is a burdensome but important part of the development process, and as such, certain types of pull requests are rejected. In general, if the **improvements** do not warrant the **review effort** required, the PR has a high chance of being rejected. It is up to the PR author to convince the reviewers that the changes warrant the review effort, and if reviewers are "Concept NAK'ing" the PR, the author may need to present arguments and/or do research backing their suggested changes.
Squashing Commits
---------------------------
If your pull request is accepted for merging, you may be asked by a maintainer

View File

@ -213,6 +213,7 @@ script: |
rm -rf ${DISTNAME}/lib/pkgconfig
find ${DISTNAME}/bin -type f -executable -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \;
find ${DISTNAME}/lib -type f -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \;
cp ../doc/README.md ${DISTNAME}/
find ${DISTNAME} -not -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
find ${DISTNAME} -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz
cd ../../

View File

@ -5,7 +5,7 @@ suites:
architectures:
- "amd64"
packages:
- "libssl-dev"
- "libssl-dev" # do not merge bitcoin#13782, see https://github.com/dashpay/dash/pull/3894
- "autoconf"
- "automake"
- "libtool"

View File

@ -112,7 +112,7 @@ struct CDiskBlockPos
std::string ToString() const
{
return strprintf("CBlockDiskPos(nFile=%i, nPos=%i)", nFile, nPos);
return strprintf("CDiskBlockPos(nFile=%i, nPos=%i)", nFile, nPos);
}
};

View File

@ -14,10 +14,10 @@
// GCC 4.8 is missing some C++11 type_traits,
// https://www.gnu.org/software/gcc/gcc-5/changes.html
#if defined(__GNUC__) && __GNUC__ < 5
#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor
#else
#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible
#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible
#endif
#ifdef WIN32

View File

@ -51,8 +51,8 @@ static void SetupCliArgs()
gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdinrpcpass", strprintf("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();

View File

@ -535,7 +535,7 @@ HTTPRequest::~HTTPRequest()
// evhttpd cleans up the request, as long as a reply was sent.
}
std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr)
std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr) const
{
const struct evkeyvalq* headers = evhttp_request_get_input_headers(req);
assert(headers);
@ -608,7 +608,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
req = nullptr; // transferred back to main thread
}
CService HTTPRequest::GetPeer()
CService HTTPRequest::GetPeer() const
{
evhttp_connection* con = evhttp_request_get_connection(req);
CService peer;
@ -622,12 +622,12 @@ CService HTTPRequest::GetPeer()
return peer;
}
std::string HTTPRequest::GetURI()
std::string HTTPRequest::GetURI() const
{
return evhttp_request_get_uri(req);
}
HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod()
HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const
{
switch (evhttp_request_get_command(req)) {
case EVHTTP_REQ_GET:

View File

@ -74,21 +74,21 @@ public:
/** Get requested URI.
*/
std::string GetURI();
std::string GetURI() const;
/** Get CService (address:ip) for the origin of the http request.
*/
CService GetPeer();
CService GetPeer() const;
/** Get request method.
*/
RequestMethod GetRequestMethod();
RequestMethod GetRequestMethod() const;
/**
* Get the request header specified by hdr, or an empty string.
* Return a pair (isPresent,string).
*/
std::pair<bool, std::string> GetHeader(const std::string& hdr);
std::pair<bool, std::string> GetHeader(const std::string& hdr) const;
/**
* Read request body.

View File

@ -104,7 +104,7 @@ WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx)
WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()),
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity();
result.depth_in_main_chain = wtx.GetDepthInMainChain();
result.time_received = wtx.nTimeReceived;

View File

@ -265,32 +265,32 @@ public:
prevector() : _size(0), _union{{}} {}
explicit prevector(size_type n) : _size(0) {
explicit prevector(size_type n) : prevector() {
resize(n);
}
explicit prevector(size_type n, const T& val = T()) : _size(0) {
explicit prevector(size_type n, const T& val) : prevector() {
change_capacity(n);
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) : _size(0) {
prevector(InputIterator first, InputIterator last) : prevector() {
size_type n = last - first;
change_capacity(n);
_size += n;
fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
prevector(const prevector<N, T, Size, Diff>& other) : prevector() {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) : _size(0) {
prevector(prevector<N, T, Size, Diff>&& other) : prevector() {
swap(other);
}

View File

@ -10,8 +10,7 @@ class CBlockIndex;
class UniValue;
/**
* Get the difficulty of the net wrt to the given block index, or the chain tip if
* not provided.
* Get the difficulty of the net wrt to the given block index.
*
* @return A floating point number that is a multiple of the main net minimum
* difficulty (4295032833 hashes).

View File

@ -138,11 +138,13 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
// the callbacks should run in exactly the order in which they were enqueued
for (int i = 0; i < 100; ++i) {
queue1.AddToProcessQueue([i, &counter1]() {
BOOST_CHECK_EQUAL(i, counter1++);
bool expectation = i == counter1++;
assert(expectation);
});
queue2.AddToProcessQueue([i, &counter2]() {
BOOST_CHECK_EQUAL(i, counter2++);
bool expectation = i == counter2++;
assert(expectation);
});
}

View File

@ -61,6 +61,8 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
BOOST_ERROR("Read incorrect tx");
}
}
txindex.Stop(); // Stop thread before calling destructor
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -12,7 +12,7 @@
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
//
// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety
// See https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// for documentation. The clang compiler can do advanced static analysis
// of locking when given the -Wthread-safety option.
#define LOCKABLE __attribute__((lockable))

View File

@ -34,6 +34,7 @@
#include <boost/signals2/signal.hpp>
class CBlockIndex;
extern CCriticalSection cs_main;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF;
@ -580,7 +581,7 @@ public:
bool removeSpentIndex(const uint256 txhash);
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey);

View File

@ -324,7 +324,11 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// Confirm ListCoins initially returns 1 coin grouped under coinbaseKey
// address.
auto list = wallet->ListCoins();
std::map<CTxDestination, std::vector<COutput>> list;
{
LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
@ -337,7 +341,10 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
// coinbaseKey pubkey, even though the change address has a different
// pubkey.
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
list = wallet->ListCoins();
{
LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
@ -363,7 +370,10 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
list = wallet->ListCoins();
{
LOCK2(cs_main, wallet->cs_wallet);
list = wallet->ListCoins();
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);

View File

@ -118,14 +118,25 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
/** A class to identify which pubkeys a script and a keystore have in common. */
class CAffectedKeysVisitor : public boost::static_visitor<void> {
private:
const CKeyStore &keystore;
std::vector<CKeyID> &vKeys;
public:
/**
* @param[in] keystoreIn The CKeyStore that is queried for the presence of a pubkey.
* @param[out] vKeysIn A vector to which a script's pubkey identifiers are appended if they are in the keystore.
*/
CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
/**
* Apply the visitor to each destination in a script, recursively to the redeemscript
* in the case of p2sh destinations.
* @param[in] script The CScript from which destinations are extracted.
* @post Any CKeyIDs that script and keystore have in common are appended to the visitor's vKeys.
*/
void Process(const CScript &script) {
txnouttype type;
std::vector<CTxDestination> vDest;
@ -2989,20 +3000,12 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
{
// TODO: Add AssertLockHeld(cs_wallet) here.
//
// Because the return value from this function contains pointers to
// CWalletTx objects, callers to this function really should acquire the
// cs_wallet lock before calling it. However, the current caller doesn't
// acquire this lock yet. There was an attempt to add the missing lock in
// https://github.com/bitcoin/bitcoin/pull/10340, but that change has been
// postponed until after https://github.com/bitcoin/bitcoin/pull/10244 to
// avoid adding some extra complexity to the Qt code.
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
LOCK2(cs_main, cs_wallet);
AvailableCoins(availableCoins);
for (auto& coin : availableCoins) {

View File

@ -83,6 +83,8 @@ class CWalletTx;
struct FeeCalculation;
enum class FeeEstimateMode;
extern CCriticalSection cs_main;
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
@ -877,7 +879,7 @@ public:
/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
std::map<CTxDestination, std::vector<COutput>> ListCoins() const;
std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
/**
* Find non-change parent output.

View File

@ -14,7 +14,7 @@ import re
import sys
# Matches on the date format at the start of the log event
TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z")
TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?Z")
LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event'])
@ -84,11 +84,17 @@ def get_log_events(source, logfile):
if time_match:
if event:
yield LogEvent(timestamp=timestamp, source=source, event=event.rstrip())
event = line
timestamp = time_match.group()
if time_match.group(1) is None:
# timestamp does not have microseconds. Add zeroes.
timestamp_micro = timestamp.replace("Z", ".000000Z")
line = line.replace(timestamp, timestamp_micro)
timestamp = timestamp_micro
event = line
# if it doesn't have a timestamp, it's a continuation line of the previous log.
else:
event += "\n" + line
# Add the line. Prefix with space equivalent to the source + timestamp so log lines are aligned
event += " " + line
# Flush the final event
yield LogEvent(timestamp=timestamp, source=source, event=event.rstrip())
except FileNotFoundError:
@ -107,7 +113,11 @@ def print_logs(log_events, color=False, html=False):
colors["reset"] = "\033[0m" # Reset font color
for event in log_events:
print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, event.event, colors["reset"]))
lines = event.event.splitlines()
print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
if len(lines) > 1:
for line in lines[1:]:
print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
else:
try:

View File

@ -22,7 +22,7 @@ class ReindexTest(BitcoinTestFramework):
self.nodes[0].generate(3)
blockcount = self.nodes[0].getblockcount()
self.stop_nodes()
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex"]]
self.start_nodes(extra_args)
wait_until(lambda: self.nodes[0].getblockcount() == blockcount)
self.log.info("Success")

View File

@ -36,7 +36,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
'-checkmempool',
'-txindex',
'-reindex', # Need reindex for txindex
'-acceptnonstdtxn=0', # Try to mimic main-net

View File

@ -11,11 +11,10 @@ that spend (directly or indirectly) coinbase transactions.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
# Create one-input, one-output, no-fee transaction:
class MempoolCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [["-checkmempool"]] * 2
alert_filename = None # Set by setup_network

View File

@ -7,11 +7,10 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
# Create one-input, one-output, no-fee transaction:
class MempoolCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-checkmempool"]]
def run_test(self):
node0_address = self.nodes[0].getnewaddress()

View File

@ -15,11 +15,10 @@ but less mature coinbase spends are NOT.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
# Create one-input, one-output, no-fee transaction:
class MempoolSpendCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-checkmempool"]]
def run_test(self):
chain_height = self.nodes[0].getblockcount()

31
test/functional/rpc_help.py Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test RPC help output."""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
class HelpRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
def run_test(self):
node = self.nodes[0]
# wrong argument count, note: Dash's help allows for two options since we utilize subcommands
assert_raises_rpc_error(-1, 'help', node.help, 'foo', 'bar', 'foobar')
# invalid argument
assert_raises_rpc_error(-1, 'JSON value is not a string as expected', node.help, 0)
# help of unknown command
assert_equal(node.help('foo'), 'help: unknown command: foo')
# command titles
titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')]
assert_equal(titles, ['Addressindex', 'Blockchain', 'Control', 'Dash', 'Evo', 'Generating', 'Mining', 'Network', 'Rawtransactions', 'Util', 'Wallet', 'Zmq'])
if __name__ == '__main__':
HelpRpcTest().main()

View File

@ -107,7 +107,7 @@ def all_interfaces():
max_possible *= 2
else:
break
namestr = names.tostring()
namestr = names.tobytes()
return [(namestr[i:i+16].split(b'\0', 1)[0],
socket.inet_ntoa(namestr[i+20:i+24]))
for i in range(0, outbytes, struct_size)]

View File

@ -190,6 +190,7 @@ BASE_SCRIPTS = [
'p2p_node_network_limited.py',
'feature_blocksdir.py',
'feature_config_args.py',
'rpc_help.py',
'feature_help.py',
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time

23
test/lint/lint-assertions.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Check for assertions with obvious side effects.
export LC_ALL=C
EXIT_CODE=0
# PRE31-C (SEI CERT C Coding Standard):
# "Assertions should not contain assignments, increment, or decrement operators."
OUTPUT=$(git grep -E '[^_]assert\(.*(\+\+|\-\-|[^=!<>]=[^=!<>]).*\);' -- "*.cpp" "*.h")
if [[ ${OUTPUT} != "" ]]; then
echo "Assertions should not have side effects:"
echo
echo "${OUTPUT}"
EXIT_CODE=1
fi
exit ${EXIT_CODE}

View File

@ -131,6 +131,32 @@ def parse_function_call_and_arguments(function_name, function_call):
['foo(', '123', ')']
>>> parse_function_call_and_arguments("foo", 'foo("foo")')
['foo(', '"foo"', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);')
['strprintf(', '"%s (%d)",', ' std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf),', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo<wchar_t>().to_bytes(buf), err);')
['strprintf(', '"%s (%d)",', ' foo<wchar_t>().to_bytes(buf),', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo().to_bytes(buf), err);')
['strprintf(', '"%s (%d)",', ' foo().to_bytes(buf),', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo << 1, err);')
['strprintf(', '"%s (%d)",', ' foo << 1,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo<bar>() >> 1, err);')
['strprintf(', '"%s (%d)",', ' foo<bar>() >> 1,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo < 1 ? bar : foobar, err);')
['strprintf(', '"%s (%d)",', ' foo < 1 ? bar : foobar,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo < 1, err);')
['strprintf(', '"%s (%d)",', ' foo < 1,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo > 1 ? bar : foobar, err);')
['strprintf(', '"%s (%d)",', ' foo > 1 ? bar : foobar,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo > 1, err);')
['strprintf(', '"%s (%d)",', ' foo > 1,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo <= 1, err);')
['strprintf(', '"%s (%d)",', ' foo <= 1,', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo <= bar<1, 2>(1, 2), err);')
['strprintf(', '"%s (%d)",', ' foo <= bar<1, 2>(1, 2),', ' err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo>foo<1,2>(1,2)?bar:foobar,err)');
['strprintf(', '"%s (%d)",', ' foo>foo<1,2>(1,2)?bar:foobar,', 'err', ')']
>>> parse_function_call_and_arguments("strprintf", 'strprintf("%s (%d)", foo>foo<1,2>(1,2),err)');
['strprintf(', '"%s (%d)",', ' foo>foo<1,2>(1,2),', 'err', ')']
"""
assert(type(function_name) is str and type(function_call) is str and function_name)
remaining = normalize(escape(function_call))
@ -139,9 +165,10 @@ def parse_function_call_and_arguments(function_name, function_call):
parts = [expected_function_call]
remaining = remaining[len(expected_function_call):]
open_parentheses = 1
open_template_arguments = 0
in_string = False
parts.append("")
for char in remaining:
for i, char in enumerate(remaining):
parts.append(parts.pop() + char)
if char == "\"":
in_string = not in_string
@ -159,6 +186,15 @@ def parse_function_call_and_arguments(function_name, function_call):
parts.append(parts.pop()[:-1])
parts.append(char)
break
prev_char = remaining[i - 1] if i - 1 >= 0 else None
next_char = remaining[i + 1] if i + 1 <= len(remaining) - 1 else None
if char == "<" and next_char not in [" ", "<", "="] and prev_char not in [" ", "<"]:
open_template_arguments += 1
continue
if char == ">" and next_char not in [" ", ">", "="] and prev_char not in [" ", ">"] and open_template_arguments > 0:
open_template_arguments -= 1
if open_template_arguments > 0:
continue
if char == ",":
parts.append("")
return parts

View File

@ -28,7 +28,7 @@ import sys
def main():
config = configparser.ConfigParser()
config.optionxform = str
config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"), encoding="utf8"))
config.read_file(open(os.path.join(os.path.dirname(__file__), "../config.ini"), encoding="utf8"))
env_conf = dict(config.items('environment'))
parser = argparse.ArgumentParser(description=__doc__)